Compare commits
1346 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
068da5fc96 | ||
|
|
a0999aba79 | ||
|
|
8eb8eaf5a1 | ||
|
|
66e9eb3ed3 | ||
|
|
844f6e5e1e | ||
|
|
1398a8606f | ||
|
|
88ef893540 | ||
|
|
825ec2b143 | ||
|
|
66daa8960a | ||
|
|
20f7259a47 | ||
|
|
be15d62632 | ||
|
|
903506ca9a | ||
|
|
73ed8cc11f | ||
|
|
303e8d790a | ||
|
|
2b74236c8c | ||
|
|
c0b3262be4 | ||
|
|
45f23ebc82 | ||
|
|
1d3ddd8f52 | ||
|
|
a77022dcd0 | ||
|
|
99cb54a426 | ||
|
|
41c4363f11 | ||
|
|
e40a667e3c | ||
|
|
519c48149f | ||
|
|
6fb60ef9cf | ||
|
|
65c941a805 | ||
|
|
63dfbd178b | ||
|
|
c49110572d | ||
|
|
acfec3e9af | ||
|
|
1c37edbee6 | ||
|
|
9a1d03d90d | ||
|
|
c3d1cbd7c2 | ||
|
|
f2fad82a97 | ||
|
|
7a9370612f | ||
|
|
e32920645c | ||
| 4bde4efa9d | |||
| eacb0c9af4 | |||
| 8c444ef75d | |||
| 9c9e9532ac | |||
| 2df210f87a | |||
|
|
a05ec05351 | ||
|
|
5279de73ab | ||
|
|
84f99f17fc | ||
| d025aa3cf6 | |||
|
|
d072086c56 | ||
| 38caf84b2d | |||
|
|
112d4f82d3 | ||
|
|
319df10cfe | ||
|
|
26051d70d2 | ||
|
|
2cde1280a4 | ||
|
|
ef248b684d | ||
| 38a4976b58 | |||
|
|
73dbbf79bb | ||
|
|
3e996d9bd9 | ||
| 6619c20b3b | |||
| f3e6e1c6d2 | |||
| 6000743c45 | |||
| 1150554ad5 | |||
|
|
ac2e9954ed | ||
|
|
beda4c19f8 | ||
|
|
412b13fb1c | ||
|
|
8c3e1058d4 | ||
|
|
ad1a1c54aa | ||
|
|
afc8e4e165 | ||
|
|
47ad73ab57 | ||
|
|
4407f8c404 | ||
|
|
5fa9b70766 | ||
|
|
8585407df9 | ||
|
|
f2dd2230c4 | ||
|
|
578ca6670d | ||
| 33398e082c | |||
| 20766d16e1 | |||
| 315c988393 | |||
|
|
6fcfeda58d | ||
| 6e6d24ea45 | |||
|
|
7124eaac74 | ||
|
|
5be42be128 | ||
|
|
e68bb707b2 | ||
|
|
42172c6575 | ||
|
|
1a440d7d12 | ||
|
|
25a825e41c | ||
| 155dad9994 | |||
|
|
38e99c69af | ||
|
|
41fcf1d4cf | ||
|
|
5ccfa5cdd9 | ||
|
|
0c807bd912 | ||
|
|
294f9d9ef2 | ||
|
|
42f396a992 | ||
|
|
57de23c1fb | ||
|
|
dfed7a844a | ||
|
|
a2c5ece991 | ||
|
|
2b4292abcc | ||
| d5762fda3c | |||
|
|
99f6fa66a1 | ||
|
|
35b58d0037 | ||
| 770ae31556 | |||
| 346c936e14 | |||
| fc6e200afe | |||
| 308627c9aa | |||
|
|
1347694672 | ||
|
|
1898c2fc71 | ||
|
|
00139bdd96 | ||
|
|
d438344943 | ||
|
|
53bc8d6b0e | ||
|
|
da33f6276a | ||
|
|
a715eed7d9 | ||
|
|
8da81e123e | ||
|
|
8c2cd49ffa | ||
|
|
2772fd8e91 | ||
|
|
10b79f2041 | ||
|
|
dde8d24461 | ||
|
|
48b3b7f641 | ||
|
|
c7cfae9397 | ||
| 15d9df16ee | |||
|
|
4407bec88c | ||
|
|
d2939dbf22 | ||
|
|
ec5d14e8e2 | ||
|
|
607fd303bb | ||
|
|
0ef2341386 | ||
|
|
08cfcbfb46 | ||
|
|
4705c95fb2 | ||
|
|
f2502fa037 | ||
|
|
72f7df520a | ||
|
|
4dc9afc1f8 | ||
|
|
973c1f596d | ||
|
|
cc14841afb | ||
|
|
effbd3cf38 | ||
|
|
e852f7fd42 | ||
|
|
ccd9481969 | ||
|
|
ae9604db21 | ||
|
|
88231bc3a1 | ||
|
|
6dcad4424c | ||
|
|
9ba136acd4 | ||
|
|
f5f42c3643 | ||
|
|
65d6f8e947 | ||
|
|
e8c89935ab | ||
|
|
e980564d64 | ||
|
|
19282b47d1 | ||
|
|
e86b6b8507 | ||
|
|
9d79960ef6 | ||
|
|
67538b0ee0 | ||
|
|
9dc9b776d5 | ||
|
|
80274175ec | ||
|
|
87e5fa3bd9 | ||
|
|
7a09815a84 | ||
|
|
089000df30 | ||
|
|
2530fc7e8b | ||
|
|
bb07482efc | ||
|
|
004f228428 | ||
|
|
f31555044a | ||
|
|
f7b557c6b0 | ||
|
|
9f444dd718 | ||
|
|
a39869c43c | ||
|
|
5395c76d98 | ||
|
|
77a856e84e | ||
|
|
b4802a7f4d | ||
|
|
8d39e5a430 | ||
|
|
d56dd2f921 | ||
|
|
6dfdc44c4e | ||
|
|
26d28166f8 | ||
|
|
0640f90083 | ||
|
|
957ae1c50a | ||
|
|
16b277fe1a | ||
|
|
73fcd895db | ||
|
|
15918fdb89 | ||
|
|
932060afcc | ||
|
|
640a3bf777 | ||
|
|
6092d1d406 | ||
|
|
3f97d7bb84 | ||
|
|
209e7ff251 | ||
|
|
4739e06473 | ||
|
|
67e592670c | ||
|
|
4f828f188d | ||
|
|
74ee335758 | ||
|
|
bd57ac8938 | ||
|
|
b756c36f73 | ||
|
|
024b3ea6e8 | ||
|
|
97d2c2c3c5 | ||
|
|
b9d46c7f92 | ||
|
|
a1f1cd255b | ||
|
|
834ddc8218 | ||
|
|
4b0c3215fd | ||
|
|
acb81aeded | ||
|
|
56b3d800e3 | ||
|
|
030463f7ff | ||
|
|
d8ce4b395f | ||
|
|
fa143c2e63 | ||
|
|
406d9c7407 | ||
|
|
8c7ca97971 | ||
|
|
32ebcb4c06 | ||
|
|
b12f3d0b4d | ||
|
|
d40672d946 | ||
|
|
1fc2f18358 | ||
|
|
74533d18ff | ||
|
|
63ab589b8c | ||
|
|
8154accf1b | ||
|
|
05ad1a5d96 | ||
|
|
a88fbaf8c8 | ||
|
|
aa543d0c57 | ||
|
|
d5ab0cf716 | ||
|
|
4e6b89afd2 | ||
|
|
8e8aa263e9 | ||
|
|
178af9a9fa | ||
|
|
4232a5923f | ||
|
|
d2e6af03d1 | ||
|
|
d3ba090b1a | ||
|
|
e762275a96 | ||
|
|
7672ab9d44 | ||
|
|
66189452ac | ||
|
|
a128762279 | ||
|
|
612d07218e | ||
|
|
45fde86060 | ||
|
|
b5d94e8fdc | ||
|
|
c3d1a5e1ee | ||
|
|
fdf23c9956 | ||
|
|
eecd72de04 | ||
|
|
a09b5bd64e | ||
|
|
ca3bbca1b1 | ||
|
|
17afe29124 | ||
|
|
8e42afe72e | ||
|
|
923cb823a3 | ||
|
|
6eda5ec53c | ||
|
|
34353d7545 | ||
|
|
9d84125232 | ||
|
|
faaae7d2f2 | ||
|
|
97b368412c | ||
|
|
ef4a5b7fd1 | ||
|
|
e5020f8d57 | ||
|
|
3fd60dedf9 | ||
|
|
78ba99f009 | ||
|
|
68f8098016 | ||
|
|
a0dcd1ebab | ||
|
|
25d009587e | ||
|
|
0c7c6b88f3 | ||
|
|
6552cfdc38 | ||
|
|
665c32dbb4 | ||
|
|
6609afd006 | ||
|
|
969afdec7f | ||
|
|
ecaa355bab | ||
|
|
ad9e1e9950 | ||
|
|
d1b3d40b47 | ||
|
|
8efdec785e | ||
|
|
6a7d20dd58 | ||
|
|
d39549e3ac | ||
|
|
cb3f4b3898 | ||
|
|
2b578ae333 | ||
|
|
c11dac322d | ||
|
|
4855baa8cc | ||
|
|
7e13fab911 | ||
|
|
94d6b13f8d | ||
|
|
9eebed2d3a | ||
|
|
e01e547b62 | ||
|
|
4d7e279996 | ||
|
|
7b17796c91 | ||
|
|
ee197ef03f | ||
|
|
dbd570a9fb | ||
|
|
14b5c13d50 | ||
|
|
5b7395d6df | ||
|
|
13cebfeccc | ||
|
|
a99e0b9878 | ||
| 71b3ba15af | |||
|
|
76218f2386 | ||
|
|
8a156fe505 | ||
|
|
cc06735cff | ||
|
|
864ab31766 | ||
|
|
3557e16cd1 | ||
|
|
b275fb303d | ||
|
|
235329c3fb | ||
|
|
ce26a0d76b | ||
|
|
78455afa5d | ||
|
|
93409e759a | ||
|
|
3b3639a560 | ||
|
|
d0b1d40cf1 | ||
|
|
316f84174d | ||
|
|
ef8989ecff | ||
|
|
8a6741b350 | ||
|
|
08313e3f02 | ||
|
|
8db5ee897e | ||
|
|
911fdb4587 | ||
|
|
1013daa902 | ||
|
|
91c01ef5fe | ||
|
|
8e5bbe0955 | ||
|
|
9a2449c0c6 | ||
|
|
322145d3ac | ||
|
|
add4f41b2f | ||
|
|
a1a391b90d | ||
|
|
defecb664f | ||
|
|
b9eea417bd | ||
|
|
cd7c1de441 | ||
|
|
6b85ca08e3 | ||
|
|
c603aff3a0 | ||
|
|
ee3181a1d9 | ||
|
|
5512219777 | ||
|
|
c03990cc56 | ||
|
|
e655c94108 | ||
|
|
a097744455 | ||
|
|
cd70c91f01 | ||
|
|
ebcf600b4b | ||
|
|
fc8a9bde85 | ||
|
|
58b0db5171 | ||
|
|
92b3513804 | ||
|
|
175c7337bb | ||
|
|
3d00480443 | ||
|
|
bf15edecf9 | ||
|
|
2c7843eb87 | ||
|
|
3b52dec187 | ||
|
|
c6bcc058b2 | ||
|
|
766babbf20 | ||
|
|
68fa115c4b | ||
|
|
4d2a11ed00 | ||
|
|
14b39d5dbd | ||
|
|
a2455ca7ce | ||
|
|
3254b0e3d8 | ||
|
|
8a73015345 | ||
|
|
d68171bb46 | ||
| d1d825b11d | |||
| b913d064e5 | |||
|
|
1d9670096f | ||
|
|
04629f5521 | ||
|
|
548aaba97d | ||
|
|
73ce3bf590 | ||
| 7dec159981 | |||
| 0b62231d56 | |||
| 5ff8bd662f | |||
|
|
04eecad5b2 | ||
| f64d2760d6 | |||
| ecf579ecb3 | |||
| 69011c8696 | |||
| 8b3b4d1829 | |||
|
|
ba0cd7c661 | ||
|
|
3817d7698a | ||
| b6abde70bc | |||
| 68eb0a623a | |||
| 81d78fe23f | |||
|
|
e26a6a9272 | ||
|
|
91e08c8679 | ||
|
|
8d93ddeaf3 | ||
|
|
593a33df5d | ||
|
|
d8a4dbbc48 | ||
|
|
18bb09da45 | ||
|
|
d18a37929e | ||
|
|
a3ce0ce29a | ||
|
|
90cb0959b9 | ||
|
|
8ef203f21b | ||
|
|
0cdea5ebac | ||
|
|
f3d0639ee6 | ||
|
|
cbe4c7499b | ||
|
|
7032a92339 | ||
|
|
70e8b548ba | ||
|
|
96550c8ed4 | ||
|
|
85f9926423 | ||
| 69d3c20c7b | |||
|
|
c152871dba | ||
|
|
7690c7a166 | ||
|
|
af34d8fe2a | ||
|
|
8c5a14d166 | ||
|
|
6ed12969d3 | ||
|
|
7954a266eb | ||
|
|
1ee36944f6 | ||
|
|
35d57db1d1 | ||
|
|
e881ccb30c | ||
| 9b8cd47640 | |||
| 1214afd9ce | |||
|
|
75c7e05eac | ||
|
|
deda9d74d8 | ||
|
|
764d5b9a0c | ||
|
|
14b1ab88f1 | ||
|
|
008dcc7dc4 | ||
|
|
be9fd86f60 | ||
|
|
ef584e4bb9 | ||
|
|
6490a619a4 | ||
|
|
7f38ad9315 | ||
|
|
af121eef1a | ||
|
|
90bec72ca2 | ||
|
|
353eec999b | ||
|
|
947ef80f9f | ||
|
|
047e51b220 | ||
|
|
161a728848 | ||
|
|
cd3d479b30 | ||
|
|
f946abd2ee | ||
|
|
d7e41ad82c | ||
|
|
02acbe4ec3 | ||
|
|
dd8b404c14 | ||
|
|
b9c4d3b026 | ||
|
|
9f6787b2bc | ||
|
|
b9a4d5a2db | ||
|
|
1196526182 | ||
|
|
23093209c9 | ||
|
|
4b2ab160b6 | ||
|
|
4fdddeabd8 | ||
|
|
696331ae55 | ||
|
|
cdf13c3661 | ||
|
|
3e1f08aa96 | ||
|
|
28d6530c9c | ||
|
|
32d37f82b5 | ||
|
|
0609da8dae | ||
|
|
58cde9a4b1 | ||
|
|
6f766908db | ||
|
|
8493f8ed98 | ||
|
|
9aefb4242d | ||
|
|
424a0abe37 | ||
|
|
e01ddaca0b | ||
|
|
53f644a44b | ||
|
|
d425641138 | ||
|
|
c94699f017 | ||
|
|
e5a0da79f1 | ||
|
|
8e365eca62 | ||
|
|
f91411da95 | ||
|
|
a6def3d20e | ||
|
|
8979b8a67d | ||
|
|
4edb6aa318 | ||
|
|
4648697458 | ||
|
|
8bb9951fdd | ||
|
|
1196e2f3ed | ||
|
|
9cc3638604 | ||
|
|
add77681ad | ||
|
|
370d6c9bed | ||
|
|
6c7144f59d | ||
|
|
52795a86ed | ||
|
|
2c84309139 | ||
|
|
bfe672532d | ||
|
|
f029495d5f | ||
|
|
6420ec9c45 | ||
|
|
8a8b64b0c2 | ||
|
|
b0679e6d16 | ||
|
|
d157fc2393 | ||
|
|
a35c209f1e | ||
|
|
05e2c575fc | ||
|
|
c08cba3cdf | ||
|
|
f1a2aadc6e | ||
|
|
90824d28e5 | ||
|
|
2a64202e38 | ||
|
|
7271c132e5 | ||
|
|
f80be5f86d | ||
|
|
17cef6ad24 | ||
|
|
3141d2afd6 | ||
|
|
7616e44fcb | ||
| 6b20ae83db | |||
| 9b082fc4d9 | |||
| 1d05843315 | |||
| ebdff02e6e | |||
|
|
5f1d4f5673 | ||
|
|
43d7de1558 | ||
|
|
3c39995349 | ||
|
|
df2c9942aa | ||
|
|
1c027267e2 | ||
|
|
a61cd88f24 | ||
|
|
e52fa96c23 | ||
|
|
ba6409a7ad | ||
|
|
f9b0e4381b | ||
|
|
3e63c8e144 | ||
|
|
ab60c5c98c | ||
|
|
6e0b9bb7d9 | ||
|
|
9464cb772b | ||
|
|
16af74132a | ||
|
|
e01f8059c5 | ||
|
|
3b2d7ddf04 | ||
|
|
ecae9e8fed | ||
|
|
0924daa819 | ||
|
|
c79a993b33 | ||
|
|
bd3b03080b | ||
|
|
64d53aa2c9 | ||
|
|
f3753597ff | ||
|
|
8a7f7a7934 | ||
|
|
95bac60238 | ||
|
|
93cb294765 | ||
|
|
e3bf07cabb | ||
|
|
fb44eafb9c | ||
|
|
6d852a3b25 | ||
|
|
d9082f86ed | ||
|
|
dd990e65cb | ||
|
|
25c8701d75 | ||
|
|
e66eb74361 | ||
| ef65415b55 | |||
| 4666bbd4de | |||
| 9cd13000b7 | |||
| fe6677335f | |||
|
|
c1b8f9d687 | ||
|
|
27203ea6dd | ||
|
|
da9eb89e9a | ||
|
|
5c6d43bae5 | ||
|
|
7e6a5f9fc9 | ||
|
|
4701338c28 | ||
|
|
04f6fd68f9 | ||
|
|
07545f5156 | ||
|
|
7a183e1fa0 | ||
|
|
3f0068beb3 | ||
|
|
2407d5e289 | ||
|
|
032e891545 | ||
|
|
adc260a2b9 | ||
|
|
962450a3ab | ||
|
|
24caf74d90 | ||
|
|
1af6c9b0d1 | ||
|
|
7a6207079a | ||
|
|
5890f0e193 | ||
|
|
a630f2ae23 | ||
|
|
a0ebea086b | ||
|
|
571286a10e | ||
|
|
74371a8acb | ||
|
|
655aa2ccd1 | ||
|
|
064cb67b88 | ||
|
|
74e11c9bff | ||
|
|
84caddf2a4 | ||
|
|
97e1ab98fa | ||
|
|
b996a16be3 | ||
|
|
6668bb3060 | ||
|
|
fbca8d8396 | ||
|
|
8d77d959cc | ||
|
|
8d185cf2fc | ||
|
|
bdca1c328e | ||
|
|
db71303fb0 | ||
|
|
714a3b8727 | ||
|
|
fde76a4160 | ||
|
|
ee7fb20898 | ||
|
|
ba46921524 | ||
|
|
51b5f8b930 | ||
|
|
d460dbcc7f | ||
|
|
ef80991418 | ||
|
|
17be6e6188 | ||
|
|
b3b22ab638 | ||
|
|
95e9cd9c59 | ||
|
|
e0b65442a5 | ||
|
|
779cd3c5a0 | ||
|
|
a8b78fa051 | ||
|
|
c00c7c9c39 | ||
|
|
84062fc1cd | ||
|
|
c147c90a1e | ||
|
|
c501569dd7 | ||
|
|
663a7d2fe0 | ||
|
|
48418d46ab | ||
|
|
e6ac55a142 | ||
|
|
9a78b59ab5 | ||
|
|
ebf2cc4bc1 | ||
|
|
3929e6b49d | ||
|
|
c84bf46d7e | ||
|
|
cd51012fa0 | ||
|
|
3fb04aedac | ||
|
|
42212622ff | ||
|
|
06cb1c2388 | ||
|
|
b8b98be91b | ||
|
|
f821c2a8fe | ||
|
|
0aef4a9e25 | ||
|
|
6fb6f2383f | ||
|
|
ce763eba4b | ||
|
|
54ff3f30dd | ||
|
|
34c7a456b6 | ||
|
|
09fc46aa92 | ||
|
|
6f65034c24 | ||
|
|
fa29c5c92a | ||
|
|
442fed040f | ||
|
|
679784a3f1 | ||
|
|
30d67360a4 | ||
|
|
d760d885fd | ||
|
|
487658e22f | ||
|
|
74f819e5fb | ||
|
|
bd9cd88192 | ||
|
|
b6ba1071d1 | ||
|
|
2c69328303 | ||
|
|
d512e716c3 | ||
|
|
efcfeff790 | ||
|
|
8444b12c90 | ||
|
|
281b8da84a | ||
|
|
60d017d3f6 | ||
|
|
a0cd09c7c3 | ||
|
|
6b9ec26d02 | ||
|
|
51a8c12820 | ||
|
|
18f3b180b3 | ||
|
|
3486f77a80 | ||
|
|
0e76f9458d | ||
|
|
317943b970 | ||
|
|
701ad767c8 | ||
|
|
27bc5b2e79 | ||
|
|
ab964b94be | ||
|
|
0ea7871404 | ||
|
|
7de1d0947e | ||
|
|
8046888d99 | ||
|
|
0b20b30af6 | ||
|
|
bc7e9bb8df | ||
|
|
6499453d32 | ||
|
|
93f70d16bc | ||
|
|
fd8a2256a6 | ||
|
|
b4f54900bf | ||
|
|
0acf90433e | ||
|
|
1754a2eb14 | ||
|
|
96a944cc38 | ||
|
|
93822e6e13 | ||
|
|
5f716a7883 | ||
|
|
6867951ab4 | ||
|
|
cacb1b66bd | ||
|
|
674220c959 | ||
|
|
0d1379d62b | ||
|
|
2c71434eab | ||
|
|
e00ad9ca11 | ||
|
|
7c4f4dba48 | ||
|
|
a726c1d00d | ||
|
|
dc4ae424ce | ||
|
|
01cddd36a5 | ||
|
|
0e0956bc3e | ||
|
|
4607f8e9f9 | ||
|
|
f7d86b53e2 | ||
|
|
e485b0c5e4 | ||
|
|
e4fa4cb851 | ||
|
|
66f0851068 | ||
|
|
80d8fcc919 | ||
|
|
418e1b846c | ||
|
|
c622fe61d8 | ||
|
|
9611a0a638 | ||
|
|
eb33480797 | ||
|
|
685ec61bbb | ||
|
|
e5961ecd3a | ||
|
|
67e0e500c3 | ||
|
|
cca906e29b | ||
|
|
ba0e2a5310 | ||
|
|
8832683fc6 | ||
|
|
bd4b9eeaf1 | ||
|
|
a1f01dd489 | ||
|
|
875d2730ce | ||
|
|
be9033384e | ||
|
|
cdc1429196 | ||
|
|
83a92f9b4b | ||
|
|
23de394fc1 | ||
|
|
2ed1699fe1 | ||
|
|
48a3a65998 | ||
|
|
9f51cd968b | ||
|
|
5a491d4c5d | ||
|
|
b26c3754c5 | ||
|
|
206b3668e4 | ||
|
|
208fa196ab | ||
|
|
8e16ab25e5 | ||
|
|
d6936aa1c4 | ||
|
|
a093b3a3cb | ||
|
|
96fe29d9c7 | ||
|
|
e387404a82 | ||
|
|
27229be5b7 | ||
|
|
06bb8348a0 | ||
| cbe3e4c2ad | |||
| 91076ad408 | |||
| 89ecef5a20 | |||
| 99984d0042 | |||
| 579f6e623c | |||
| 00e07bfa5b | |||
| 5a8c09123f | |||
| 1d4ea95a80 | |||
| 8680051d29 | |||
| f383766add | |||
| eac578603d | |||
| 793eef4030 | |||
| 5d6d7a3248 | |||
| 897c5a528c | |||
| 2a81310a96 | |||
|
|
d62d07ea0b | ||
|
|
734a2898f6 | ||
|
|
4452e59a09 | ||
|
|
af5d24f531 | ||
|
|
18c974798d | ||
|
|
9f9e006955 | ||
|
|
8c789fcdcf | ||
|
|
9dfc972180 | ||
|
|
b8ab59bd84 | ||
|
|
771b3d837f | ||
|
|
7c782ccb6c | ||
|
|
5749930280 | ||
|
|
9484e846a3 | ||
|
|
b52292919b | ||
|
|
2ee2be96a3 | ||
|
|
24fc20d31b | ||
|
|
2788b19906 | ||
|
|
eea6b1ed6d | ||
|
|
9cd7ba13f7 | ||
|
|
0c66a65358 | ||
|
|
259b929493 | ||
|
|
7419611272 | ||
|
|
e50bdea0c7 | ||
|
|
aa649b28bb | ||
|
|
941d36f635 | ||
|
|
ea2ccdb914 | ||
|
|
42f20bcc67 | ||
|
|
815b543c81 | ||
|
|
1f64d99f6d | ||
|
|
b35c1568f3 | ||
|
|
c7f47819e0 | ||
|
|
532172f1bc | ||
|
|
74bc705175 | ||
|
|
118d66c771 | ||
|
|
ae0276509c | ||
|
|
9cb008a869 | ||
|
|
21f7ffae9a | ||
|
|
a359cccf96 | ||
|
|
f55b69d2ff | ||
|
|
c71920e186 | ||
|
|
a30b1c2dc1 | ||
|
|
c4b06e419f | ||
|
|
1ec22f83e1 | ||
|
|
7eade2b43c | ||
|
|
b236e65cec | ||
|
|
a2dc565992 | ||
|
|
d5853a1374 | ||
|
|
9d34f8a359 | ||
|
|
196883f813 | ||
|
|
1f6e5ca648 | ||
|
|
2011510871 | ||
|
|
184b941c4d | ||
|
|
e8fcf2e94b | ||
|
|
e3704b7e29 | ||
|
|
0284aae8ac | ||
|
|
c55e84b265 | ||
|
|
f43fa2bffb | ||
|
|
f6f95f39c7 | ||
|
|
771ac047fe | ||
|
|
b6e90096c5 | ||
|
|
094302e414 | ||
|
|
6dafbef969 | ||
|
|
093ab71ea8 | ||
|
|
e748e4aa32 | ||
|
|
67b268d1ad | ||
|
|
d384b3b63b | ||
|
|
b35f4ff7d1 | ||
|
|
b8d36719d0 | ||
|
|
195afc9ac4 | ||
|
|
14302180d1 | ||
|
|
9dd719a792 | ||
|
|
d43dd21ef1 | ||
|
|
3552b42cf5 | ||
|
|
c34570e6c4 | ||
|
|
c3f7519d6a | ||
|
|
87a3861273 | ||
|
|
958c3f12d4 | ||
|
|
80059244b9 | ||
|
|
25dfdeebcd | ||
|
|
d9af80e7d9 | ||
|
|
45ceae68ec | ||
|
|
28d8ccf66a | ||
|
|
b7f2cf0193 | ||
|
|
5f7092cec0 | ||
|
|
48b23e05e7 | ||
|
|
ffcb74e128 | ||
|
|
ae6a0791d7 | ||
|
|
3330c16dbc | ||
|
|
4b2f10fa00 | ||
|
|
5aaf4095f3 | ||
|
|
63fee69162 | ||
|
|
fc70f61194 | ||
|
|
8fc18e2468 | ||
|
|
b302e8224b | ||
|
|
a6692b0d4d | ||
|
|
1aa803a79d | ||
|
|
a070f73267 | ||
|
|
1c54e78eb8 | ||
|
|
d2b5180328 | ||
|
|
6850079cf3 | ||
|
|
7262f3a8aa | ||
|
|
c0cb577fd7 | ||
|
|
8442948dba | ||
|
|
aae4441df0 | ||
|
|
dcfee63c96 | ||
|
|
a7decc13bc | ||
|
|
f08e276977 | ||
|
|
5dcc298db2 | ||
|
|
b93c4b95ab | ||
|
|
31d0bec9fe | ||
|
|
2eb06ddd30 | ||
|
|
2bba03fa34 | ||
|
|
aa77523cad | ||
|
|
ab5567020d | ||
|
|
b8c892f922 | ||
|
|
f7cf98650d | ||
|
|
6422d8c599 | ||
|
|
2851cf7376 | ||
|
|
3766641bc3 | ||
| 9e24593e94 | |||
|
|
2a1de4e0cd | ||
| e3c75d81af | |||
| e6113135cc | |||
|
|
2e42dee0e6 | ||
|
|
72935d3f02 | ||
|
|
28fd0b8275 | ||
|
|
5ff332fa27 | ||
|
|
4f7dd719b3 | ||
|
|
cb23ca8d69 | ||
|
|
73349aee3a | ||
|
|
c6eebda626 | ||
|
|
4e06f3614f | ||
|
|
531345446f | ||
|
|
9c76fa7450 | ||
|
|
629068c576 | ||
|
|
c915abc68e | ||
|
|
18408cdc5e | ||
|
|
be7f18e888 | ||
|
|
424abb0d66 | ||
|
|
3e742fb964 | ||
|
|
b17eb0284f | ||
|
|
9f9abf97ca | ||
|
|
9d2e60a0e3 | ||
|
|
0220432e69 | ||
|
|
cb31b349c3 | ||
|
|
a291cfe0c7 | ||
|
|
26173a46f0 | ||
|
|
ba0751edc8 | ||
|
|
e311773963 | ||
|
|
91dc3b9bf4 | ||
|
|
b9d76a3d2f | ||
|
|
259f030750 | ||
|
|
31c678b6ee | ||
|
|
fdd99fe776 | ||
|
|
d7b64bbf57 | ||
|
|
434ea7b04c | ||
|
|
f82d3947ae | ||
|
|
964db3cc57 | ||
|
|
08af994fbd | ||
|
|
821cae1cc8 | ||
|
|
fd2c1b6f50 | ||
|
|
584644ba71 | ||
|
|
080244a50a | ||
|
|
a5c439b127 | ||
|
|
2ec3a422ce | ||
|
|
083f140770 | ||
|
|
1ca41e5d80 | ||
|
|
4ae47c84d5 | ||
|
|
24fe1dcbe1 | ||
|
|
a0634bf1c8 | ||
|
|
091ab1925d | ||
|
|
ac8a1537ef | ||
|
|
c97d05dc4a | ||
|
|
10de13db41 | ||
|
|
89ac466c1d | ||
|
|
b7c135faf7 | ||
|
|
0b47ac7bce | ||
|
|
404342aa54 | ||
|
|
ecb0a7775b | ||
|
|
148c948fa4 | ||
|
|
a2dce2e06c | ||
|
|
2f9ca94602 | ||
|
|
a772da39a5 | ||
|
|
ebd91b8ee2 | ||
|
|
0f634f9ee3 | ||
|
|
c84ea225bd | ||
|
|
ecd24e9702 | ||
|
|
adfa60fbc9 | ||
|
|
ca99f2f0e1 | ||
|
|
d83d6718b2 | ||
|
|
050a63c455 | ||
|
|
41320fc1b3 | ||
|
|
9d4417f66c | ||
|
|
db1a822c3b | ||
|
|
bfb4ead10d | ||
|
|
8cbb1c164c | ||
|
|
c21a54e336 | ||
|
|
e22dd924e6 | ||
|
|
dc4c9d692a | ||
|
|
e15c13feaa | ||
|
|
8c16c683a6 | ||
|
|
b7f95c4122 | ||
|
|
984ddf51b4 | ||
|
|
64a927ee80 | ||
|
|
ee6e53d65b | ||
|
|
aa7e741530 | ||
|
|
475c0ae465 | ||
|
|
4dcaaf166d | ||
|
|
13ce751255 | ||
|
|
9df0ddf8f2 | ||
|
|
5029565362 | ||
|
|
4098558a35 | ||
|
|
cad8bcbed5 | ||
|
|
2b26ea39bd | ||
|
|
1e69c5fc06 | ||
|
|
83c20a2a7e | ||
|
|
a5242bb7b3 | ||
|
|
8242b84753 | ||
| 57ab95cc70 | |||
| d0f68c8560 | |||
|
|
7f2c1ae52b | ||
|
|
ee8b72addd | ||
|
|
6daeb09160 | ||
|
|
04fa205ece | ||
|
|
3617b0735e | ||
|
|
864d5a68bb | ||
|
|
ccb6be5b11 | ||
|
|
96be400e08 | ||
|
|
e2be5a001e | ||
|
|
1d18ab0381 | ||
|
|
ddd553f76f | ||
|
|
0bfba3bf1b | ||
|
|
ca61961d22 | ||
|
|
e2fd36ed7a | ||
|
|
29f1bad2d8 | ||
|
|
16b973d54c | ||
|
|
89801ab12c | ||
|
|
5ab7b1cd3d | ||
|
|
e947baddd7 | ||
|
|
511bb5f6c9 | ||
|
|
6529a2ef16 | ||
|
|
ca6e707f66 | ||
|
|
c29df37e7b | ||
|
|
6723fdb66e | ||
|
|
5389575c07 | ||
|
|
b724c99262 | ||
|
|
7af7042572 | ||
|
|
c2fac359a5 | ||
|
|
d1c7af3cb5 | ||
|
|
fadc54bd71 | ||
|
|
a662414ba6 | ||
|
|
94e4d9e672 | ||
|
|
2e08b2566d | ||
|
|
ff8d260809 | ||
|
|
8dec1328ab | ||
|
|
b2465a7c16 | ||
|
|
0f336ab65b | ||
|
|
0ede57ff42 | ||
|
|
6a359e59c8 | ||
|
|
3f25cae135 | ||
|
|
daed7bf3ce | ||
|
|
501663e3fe | ||
|
|
dfd4a6729b | ||
|
|
7be869fe74 | ||
|
|
9f917b23c2 | ||
|
|
2c8faf7680 | ||
|
|
b7b54cba70 | ||
|
|
483e9c3dc5 | ||
|
|
b7f11232ee | ||
|
|
86bed99394 | ||
|
|
32369e56fb | ||
|
|
fd7090ce35 | ||
|
|
c631443971 | ||
|
|
58a205aec6 | ||
|
|
fdd5920f7b | ||
|
|
f681f36bc6 | ||
|
|
a43e3a45b5 | ||
|
|
5c75f08118 | ||
|
|
1061251e06 | ||
|
|
07c7404d95 | ||
|
|
b03d36bcee | ||
|
|
d56a001f16 | ||
|
|
df0af031f1 | ||
|
|
8f9fc87dff | ||
|
|
11cd3ac50a | ||
|
|
f179a1efc1 | ||
|
|
798f38a76c | ||
|
|
b29bf6c0eb | ||
|
|
05b2123fbf | ||
|
|
bbe39d9e41 | ||
|
|
115b3da2f6 | ||
|
|
ed4a66938b | ||
|
|
00f4cdfe53 | ||
|
|
43deb6cc3a | ||
|
|
a0396df9b6 | ||
|
|
6b33c912de | ||
|
|
a97d7a7b7e | ||
|
|
1b9c212899 | ||
|
|
4029c6dd86 | ||
|
|
b24886826f | ||
|
|
378bd4ebab | ||
|
|
b95a0816f0 | ||
|
|
1b7dc263e9 | ||
|
|
fe08d5be1b | ||
|
|
71c73f095a | ||
|
|
0caf03bd3e | ||
|
|
eb696d9d91 | ||
|
|
382d7a613e | ||
|
|
a81a77a996 | ||
|
|
506986fcb4 | ||
|
|
fca36e6d64 | ||
|
|
76c1a3d242 | ||
|
|
621dca8e3b | ||
|
|
3428fb8123 | ||
|
|
da1d8c2ac1 | ||
|
|
80511c13e4 | ||
|
|
1b08404975 | ||
|
|
d903b0a7d2 | ||
|
|
5ec64d39ee | ||
|
|
034a8d7b58 | ||
|
|
fd0751144e | ||
|
|
3be74df891 | ||
|
|
7e6216a1fc | ||
|
|
b212bd4cb9 | ||
|
|
0268f990d7 | ||
|
|
b1b93994c2 | ||
|
|
b4527192f5 | ||
|
|
847c49064b | ||
|
|
20796a10b3 | ||
|
|
41c3008fdd | ||
|
|
63efdbf5a3 | ||
|
|
a2f27760c6 | ||
|
|
d59fc169cd | ||
|
|
44f0c46839 | ||
|
|
47af016806 | ||
|
|
f9322380de | ||
|
|
8c4d02f755 | ||
|
|
72e1924479 | ||
|
|
60f6fa6e6b | ||
|
|
60c8d62d4b | ||
|
|
bb023e267c | ||
|
|
8f5d1a949e | ||
|
|
368b23ff8b | ||
|
|
3aa37c8e55 | ||
|
|
c9b7881add | ||
|
|
23ad568b0b | ||
|
|
4a771347bb | ||
|
|
3ba186b19c | ||
|
|
5cd5aaba35 | ||
|
|
162b39470d | ||
|
|
b4748dc966 | ||
|
|
4ddbd8b73f | ||
|
|
129ad86b8e | ||
|
|
4e218f9d81 | ||
|
|
4c1a7c0933 | ||
|
|
4690ef7c2c | ||
|
|
345b74e3ca | ||
|
|
3a6deef855 | ||
|
|
ddfe1754ca | ||
|
|
bae51f8edb | ||
|
|
a789bd67d0 | ||
|
|
11a7ecff6d | ||
|
|
e77f74df29 | ||
|
|
9159465d62 | ||
|
|
61073f79a8 | ||
|
|
f41d3eeeb7 | ||
|
|
d04d280f9d | ||
|
|
39afe5f2ad | ||
|
|
1f1e181f5d | ||
|
|
05f9f9a7b3 | ||
|
|
e1801bad43 | ||
|
|
359cf78f35 | ||
|
|
f8bf869a45 | ||
|
|
9a82e70728 | ||
|
|
c98cdfdb97 | ||
|
|
2d12162d8f | ||
|
|
7ad3542de6 | ||
|
|
0064995d2c | ||
|
|
86581222c4 | ||
|
|
ab085333ca | ||
|
|
fe8a10dd94 | ||
| 20243311e1 | |||
|
|
f6fd605300 | ||
|
|
a3678b0de7 | ||
|
|
e91e2db0dd | ||
|
|
ac38e8bc5a | ||
|
|
9cf30ad9f2 | ||
|
|
2b55b79471 | ||
|
|
e5341bcf35 | ||
|
|
c93faa9a9c | ||
|
|
b6252474e9 | ||
|
|
4013206dc2 | ||
|
|
4724b516c2 | ||
|
|
e4d07b733a | ||
| 23e1ef3b11 | |||
|
|
4759264994 | ||
|
|
80503300ad | ||
|
|
651f2029af | ||
|
|
cc721aa216 | ||
|
|
d6ae0bcdde | ||
|
|
f1dab38726 | ||
|
|
8b268aedd1 | ||
|
|
e33ef4ec52 | ||
|
|
b60e14d380 | ||
|
|
8d439c3332 | ||
|
|
b3e76e1f0a | ||
|
|
a68b7daa32 | ||
|
|
4b1741515e | ||
|
|
0d80175933 | ||
|
|
f7358cd65f | ||
|
|
3a5d17c10e | ||
|
|
3acd543630 | ||
|
|
5e72d76b6e | ||
|
|
4194cce461 | ||
|
|
b10117a849 | ||
|
|
3b041e1059 | ||
|
|
161d8b0ed0 | ||
|
|
eea4c15ad0 | ||
|
|
c6f2c928b6 | ||
|
|
e202d38b0e | ||
|
|
932e0a2b91 | ||
|
|
c6afd5ad0d | ||
|
|
3820dac6ee | ||
|
|
4581034325 | ||
|
|
13ef14c29a | ||
|
|
7739ae21f8 | ||
|
|
49be9e0ba7 | ||
|
|
bdaa4b3984 | ||
|
|
9ab8ce2153 | ||
|
|
fbe8151f50 | ||
|
|
33451ee285 | ||
|
|
dfe560150a | ||
|
|
f5c15f0c4c | ||
|
|
0443a2a3ed | ||
|
|
059994774d | ||
|
|
f171235a95 | ||
|
|
e75729cb27 | ||
|
|
3bb1417211 | ||
|
|
cc134935f3 | ||
|
|
40503d0f7e | ||
|
|
48cb939839 | ||
|
|
fe7aa27238 | ||
|
|
e9258fe4e7 | ||
|
|
2f55b71542 | ||
|
|
526ffb4c78 | ||
|
|
17c6d605ca | ||
|
|
f9cf519843 | ||
|
|
a223de9c73 | ||
|
|
37140d531e | ||
|
|
1231ac877e | ||
|
|
a73201a5a4 | ||
|
|
4a3c12e427 | ||
| d3b9096790 | |||
|
|
0394801933 | ||
|
|
79f424759c | ||
| cbdd1295c1 | |||
| a2aa5d3a58 | |||
| 1f3836d16f | |||
|
|
666dc77fad | ||
|
|
9da0a5ee0a | ||
|
|
ddbbca7462 | ||
|
|
0249fcd304 | ||
|
|
077bcc51ab | ||
|
|
e509d3caa8 | ||
|
|
78438727c4 | ||
|
|
17b76a60bb | ||
|
|
8a79c80232 | ||
|
|
33d9a5e410 | ||
|
|
f00d5dc6de | ||
|
|
8631b771d2 | ||
|
|
e8bac80c52 | ||
|
|
8056b4e959 | ||
|
|
e31d7ab906 | ||
|
|
4a311c1d64 | ||
|
|
e2cae23253 | ||
|
|
6590acc6c4 | ||
|
|
d5e59cc21f | ||
|
|
df6e3065b8 | ||
|
|
8f5edbb808 | ||
|
|
35cdc9f976 | ||
|
|
a6ec2e65dd | ||
|
|
5c027e44c1 | ||
|
|
2ead6cbc21 | ||
|
|
d991d9d248 | ||
|
|
70666817ce | ||
|
|
6615290fdf | ||
|
|
cf61d728e7 | ||
| a7502f6243 | |||
|
|
314bb010bb | ||
|
|
a081c28998 | ||
|
|
6ff9d2cb04 | ||
|
|
67be0c6762 | ||
|
|
855384f579 | ||
|
|
c7e2c8a2e2 | ||
|
|
fb05e2585c | ||
|
|
c9519e59a8 | ||
|
|
3c1fb52efb | ||
|
|
a0e5f3a474 | ||
|
|
f014128595 | ||
|
|
d29cc8632e | ||
|
|
a844e1bfb9 | ||
| d0b11a6081 | |||
|
|
434211d401 | ||
|
|
778c7e9bd8 | ||
|
|
d1f2d80581 | ||
|
|
a746bd8e09 | ||
|
|
2b7d96f25d | ||
|
|
22813d6736 | ||
| 470ea6578a | |||
| 5fefea4871 | |||
|
|
e83ef7a31d | ||
|
|
1a4ac564d4 | ||
|
|
78ae57005c | ||
|
|
12d2c9127e | ||
|
|
84150d4166 | ||
|
|
7b8d894413 | ||
|
|
7ec8a25a84 | ||
|
|
a09c5f4c25 | ||
|
|
ab9a26dc98 | ||
|
|
55fb2e073b | ||
| 86e7155ab7 | |||
|
|
280f04660e | ||
| f1f10c27a8 | |||
| d41fb86fd0 | |||
| daf4b07ef7 | |||
| 22605c6fe9 | |||
| 8aebfd9f4e | |||
| be1e2b28e0 | |||
| 11ed69dacf | |||
| 59cc5ed6c6 | |||
|
|
ae7cadee8a | ||
|
|
1001ed44f5 | ||
|
|
c69f2d9256 | ||
|
|
8b072b7c0d | ||
|
|
acfd1d0760 | ||
|
|
ceaede8cb8 | ||
|
|
034cdaf7e3 | ||
|
|
32e0bf5bde | ||
|
|
89d73974e3 | ||
|
|
89579b6e36 | ||
|
|
b4f83997e7 | ||
|
|
0d5dc6b0d4 | ||
|
|
411a5ffbd2 | ||
|
|
2e27a356f5 | ||
|
|
26d740ef77 | ||
|
|
a3d77a3fa1 | ||
|
|
1648325d69 | ||
|
|
272685dc50 | ||
|
|
04422f3da5 | ||
|
|
c2c525247c | ||
|
|
2517d8be25 | ||
|
|
3153c86f09 | ||
|
|
ae0dfbc126 | ||
|
|
a2d52b6454 | ||
|
|
ac3f03c3ba | ||
|
|
d55df3d4a0 | ||
|
|
3ec9f5a5bb | ||
| c66509f66e | |||
| 53c158bde3 | |||
| cbe41034a3 | |||
|
|
b3562ae78c | ||
|
|
ea4a7422ef | ||
|
|
97f23da0c1 | ||
|
|
84558f9447 | ||
|
|
a91b36986d | ||
| 3dc52575f0 | |||
|
|
155e6eea60 | ||
|
|
94e5b304d2 | ||
|
|
49e2e68f9b | ||
|
|
3e53136a93 | ||
|
|
7ec399c58b | ||
|
|
47fd7964e8 | ||
|
|
e3a74453a8 | ||
|
|
18d13e4252 | ||
|
|
cb1b1d352b | ||
|
|
c354657eda | ||
|
|
037cd6e733 | ||
|
|
1095994a4a | ||
|
|
ea2bf3c236 | ||
|
|
525fbb6527 | ||
|
|
5d0eab244b | ||
|
|
6b3d43846f | ||
|
|
8241546378 | ||
|
|
0434045293 | ||
|
|
34c2355a2a | ||
|
|
0faa89e3a5 | ||
|
|
c0979509b4 | ||
|
|
ef2c91c1fb | ||
|
|
fb1153d3b3 | ||
|
|
847c7f9d63 | ||
|
|
ab8b5fe4b1 | ||
|
|
700e3ff1ac | ||
|
|
a35c4ff1d8 | ||
|
|
a6ebfc1ade | ||
|
|
882294dabf | ||
|
|
adda973b56 | ||
|
|
903016ec44 | ||
|
|
380b8b0347 | ||
|
|
f9fabc5079 | ||
|
|
585513ce76 | ||
|
|
2ea9656d3f | ||
|
|
66b89de8c7 | ||
|
|
844b85018b | ||
|
|
1c1a693a2b | ||
|
|
f81e950513 | ||
|
|
ffff7606c8 | ||
|
|
bee25781b9 | ||
|
|
47bf2ec9a7 | ||
|
|
badeee48df | ||
|
|
e370e56a15 | ||
|
|
db5f1f85bb | ||
|
|
2bddc94df5 | ||
|
|
aa5ef4afb9 | ||
|
|
d99d834d87 | ||
|
|
83479f115b | ||
| 924dc7012a | |||
|
|
daaacac16f | ||
|
|
aa4d53e292 | ||
| 3febd79aa0 | |||
|
|
7e2be88dff | ||
|
|
e0e840f6d5 | ||
| 102ba5032e | |||
| b83ed3e2ca | |||
| 1e5ebe739d | |||
| 70af1c8a77 | |||
| a2962f0fe5 | |||
| cf057c4df9 | |||
| de6b7772e4 | |||
| 43551c9a7a | |||
| 23adf6e4a7 | |||
| f6d123beb8 | |||
| 27996c9003 | |||
|
|
bec24fe4a4 | ||
|
|
e71f667114 | ||
|
|
8300de7f11 | ||
|
|
017c1c2c46 | ||
|
|
6fe015f134 | ||
|
|
3727c38182 | ||
| a75903efe5 | |||
|
|
d8bfe46bd8 | ||
|
|
2e3a0b4483 | ||
|
|
49db707a9f | ||
|
|
f2aaab84cc | ||
|
|
fc80a25685 | ||
|
|
f47abf90ac | ||
|
|
5bf6e89a0a | ||
|
|
7846b0c469 | ||
|
|
9153fd8f8a | ||
|
|
57cf52838c | ||
|
|
c14eee1bd4 | ||
|
|
2c71467ced | ||
| 02d74a4d3e | |||
|
|
4a0677fd14 | ||
|
|
800bb3b872 | ||
|
|
89170a13a6 | ||
|
|
ca23263655 | ||
|
|
936bb3bae5 | ||
| a90d18caf1 | |||
|
|
918075daaf | ||
|
|
fac3eb4099 | ||
|
|
76ccd1a118 | ||
|
|
0315408e05 | ||
|
|
722401c2e4 | ||
|
|
efa4c148c9 | ||
|
|
dcd65feb21 | ||
|
|
b2c4749962 | ||
|
|
941ffceb1b | ||
|
|
ac1111c251 | ||
|
|
992e48b84e | ||
|
|
68e38bc6b8 | ||
|
|
81acfc7da8 | ||
|
|
107587de00 | ||
|
|
22f85e0d3b | ||
|
|
84d2ce8a52 | ||
|
|
1df50af368 | ||
|
|
1b9a80a442 | ||
|
|
97e98c4d31 | ||
|
|
e33720e9c0 | ||
|
|
ea588a67df | ||
|
|
4e507e9805 | ||
|
|
1f5d1a9765 | ||
|
|
0e9656cc1d | ||
|
|
ec2b0f7084 | ||
|
|
d12785af6c | ||
|
|
5f8a5d8dc4 | ||
|
|
b4f4331f26 | ||
|
|
fd217dfdc9 | ||
|
|
0f4cf714ac | ||
|
|
f2deb06db3 | ||
|
|
4a5d1e5ee0 | ||
|
|
08b8fd0ae2 | ||
|
|
5c540e0475 | ||
|
|
a940870cac | ||
|
|
351eafa06c | ||
|
|
841c4685ff | ||
|
|
3d816ac2c0 | ||
|
|
5022a8b1a2 |
334 changed files with 58719 additions and 23181 deletions
25
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
25
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: "[BUG]"
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is. Give the `ulab` version
|
||||
|
||||
```python
|
||||
import ulab
|
||||
print(ulab.__version__)
|
||||
```
|
||||
|
||||
**To Reproduce**
|
||||
Describe the steps to reproduce the behavior.
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Additional context**
|
||||
Add any other context that might help to locate the root of the problem.
|
||||
14
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
14
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: "[FEATURE REQUEST]"
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen. If possible, link to the `numpy/scipy` function.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the feature request here.
|
||||
98
.github/workflows/build.yml
vendored
Normal file
98
.github/workflows/build.yml
vendored
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
name: Build CI
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'code/**'
|
||||
- 'tests/**'
|
||||
- '.github/workflows/**'
|
||||
- 'build*.sh'
|
||||
- 'requirements*.txt'
|
||||
release:
|
||||
types: [published]
|
||||
check_suite:
|
||||
types: [rerequested]
|
||||
|
||||
jobs:
|
||||
micropython:
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-24.04
|
||||
- macOS-latest
|
||||
dims: [1, 2, 3, 4]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Dump GitHub context
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Install requirements
|
||||
run: |
|
||||
if type -path apt-get; then
|
||||
sudo apt update && sudo apt-get install gcc-multilib
|
||||
fi
|
||||
|
||||
- name: Versions
|
||||
run: |
|
||||
gcc --version
|
||||
python3 --version
|
||||
- name: Checkout ulab
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Checkout micropython repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: micropython/micropython
|
||||
path: micropython
|
||||
|
||||
- name: Run build.sh
|
||||
run: ./build.sh ${{ matrix.dims }}
|
||||
|
||||
circuitpython:
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-24.04
|
||||
- macOS-latest
|
||||
dims: [1, 2, 3, 4]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Dump GitHub context
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Versions
|
||||
run: |
|
||||
gcc --version
|
||||
python3 --version
|
||||
|
||||
- name: Checkout ulab
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install requirements
|
||||
run: |
|
||||
if type -path apt-get; then
|
||||
sudo apt update && sudo apt-get install gettext librsvg2-bin
|
||||
else
|
||||
brew install gettext librsvg
|
||||
echo >>$GITHUB_PATH /usr/local/opt/gettext/bin
|
||||
echo >>$GITHUB_PATH /usr/local/opt/librsvg/bin
|
||||
fi
|
||||
python3 -mpip install --upgrade -r requirements_cp_dev.txt
|
||||
|
||||
- name: Run build-cp.sh
|
||||
run: ./build-cp.sh ${{ matrix.dims }}
|
||||
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/micropython
|
||||
/circuitpython
|
||||
/*.exp
|
||||
/*.out
|
||||
/docs/manual/build/
|
||||
/docs/manual/source/**/*.pyi
|
||||
/docs/.ipynb_checkpoints/
|
||||
/docs/ulab-test.ipynb
|
||||
/code/.atom-build.yml
|
||||
build/micropython
|
||||
build/ulab
|
||||
|
||||
.idea
|
||||
24
.readthedocs.yaml
Normal file
24
.readthedocs.yaml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# .readthedocs.yaml
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Set the version of Python and other tools you might need
|
||||
build:
|
||||
os: ubuntu-20.04
|
||||
tools:
|
||||
python: "3.9"
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/manual/source/conf.py
|
||||
|
||||
# If using Sphinx, optionally build your docs in additional formats such as PDF
|
||||
formats: all
|
||||
|
||||
# Optionally declare the Python requirements required to build your docs
|
||||
python:
|
||||
install:
|
||||
- requirements: requirements.txt
|
||||
17
CONTRIBUTING.md
Normal file
17
CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
Contributions of any kind are always welcome.
|
||||
|
||||
# Contributing to the code base
|
||||
|
||||
If you feel like adding to the code, you can simply issue a pull request. If you do so, please, try to adhere to `micropython`'s [coding conventions](https://github.com/micropython/micropython/blob/master/CODECONVENTIONS.md#c-code-conventions).
|
||||
|
||||
# Documentation
|
||||
|
||||
However, you can also contribute to the documentation (preferably via the [jupyter notebooks](https://github.com/v923z/micropython-ulab/tree/master/docs).
|
||||
|
||||
## Testing
|
||||
|
||||
If you decide to lend a hand with testing, here are the steps:
|
||||
|
||||
1. Write a test script that checks a particular function, or a set of related functions!
|
||||
1. Drop this script in one of the folders in [ulab tests](https://github.com/v923z/micropython-ulab/tree/master/tests)!
|
||||
1. Run the [./build.sh](https://github.com/v923z/micropython-ulab/blob/master/build.sh) script in the root directory of `ulab`! This will clone the latest `micropython`, compile the firmware for `unix`, execute all scripts in the `ulab/tests`, and compare the results to those in the expected results files, which are also in `ulab/tests`, and have an extension `.exp`. In case you have a new snippet, i.e., you have no expected results file, or if the results differ from those in the expected file, a new expected file will be generated in the root directory. You should inspect the contents of this file, and if they are satisfactory, then the file can be moved to the `ulab/tests` folder, alongside your snippet.
|
||||
452
README.md
452
README.md
|
|
@ -1,17 +1,447 @@
|
|||
# micropython-ulab
|
||||
# ulab
|
||||
|
||||
ulab is a numpy-like array manipulation library for micropython.
|
||||
The module is written in C, defines compact containers for numerical
|
||||
data, and is fast.
|
||||
[](https://micropython-ulab.readthedocs.io/en/latest/index.html)
|
||||
|
||||
Documentation can be found under https://micropython-ulab.readthedocs.io/en/latest/
|
||||
The source for the manual is in https://github.com/v923z/micropython-ulab/blob/master/docs/ulab-manual.ipynb,
|
||||
while developer help is in https://github.com/v923z/micropython-ulab/blob/master/docs/ulab.ipynb.
|
||||
`ulab` is a `numpy`-like array manipulation library for [micropython](http://micropython.org/) and [CircuitPython](https://circuitpython.org/).
|
||||
The module is written in C, defines compact containers (`ndarray`s) for numerical data of one to four
|
||||
dimensions, and is fast. The library is a software-only standard `micropython` user module,
|
||||
i.e., it has no hardware dependencies, and can be compiled for any platform. 8-, and 16-bit signed
|
||||
and unsigned integer `dtypes`, as well as `float`, and, optionally, ` complex` are supported.
|
||||
The `float` implementation of `micropython` (32-bit `float`, or 64-bit `double`) is automatically
|
||||
detected and handled.
|
||||
|
||||
Firmware for pyboard.v.1.1, as well as for PYBD_SF6 is updated once
|
||||
in a while, and can be downloaded from https://github.com/v923z/micropython-ulab/releases,
|
||||
otherwise, it can be compiled from the source by following the steps
|
||||
https://micropython-usermod.readthedocs.io/en/latest/usermods_05.html#compiling-our-module.
|
||||
1. [Supported functions and methods](#supported-functions-and-methods)
|
||||
1. [ndarray methods](#ndarray-methods)
|
||||
2. [numpy and scipy functions](#numpy-and-scipy-functions)
|
||||
3. [ulab utilities](#ulab-utilities)
|
||||
4. [user module](#user-module)
|
||||
4. [Usage](#usage)
|
||||
5. [Finding help](#finding-help)
|
||||
6. [Benchmarks](#benchmarks)
|
||||
7. [Firmware](#firmware)
|
||||
1. [Customising the firmware](#customising-the-firmware)
|
||||
1. [Platforms including ulab](#platforms-including-ulab)
|
||||
1. [Compiling](#compiling)
|
||||
1. [UNIX](#unix-port)
|
||||
1. [STM-based boards](#stm-based-boards)
|
||||
1. [ESP32-based boards](#esp32-based-boards)
|
||||
1. [RP2-based boards](#rp2-based-boards)
|
||||
1. [Compiling for CircuitPython](#compiling-for-circuitpython)
|
||||
8. [Issues, contributing, and testing](#issues-contributing-and-testing)
|
||||
1. [Testing](#testing)
|
||||
|
||||
# Supported functions and methods
|
||||
|
||||
|
||||
## ndarray methods
|
||||
|
||||
`ulab` implements `numpy`'s `ndarray` with the `==`, `!=`, `<`, `<=`, `>`, `>=`, `+`, `-`, `/`, `*`, `**`,
|
||||
`%`, `+=`, `-=`, `*=`, `/=`, `**=`, `%=` binary operators, and the `len`, `~`, `-`, `+`, `abs` unary operators that
|
||||
operate element-wise. Type-aware `ndarray`s can be initialised from any `micropython` iterable, lists of
|
||||
iterables via the `array` constructor, or by means of the `arange`, `concatenate`, `diag`, `eye`,
|
||||
`frombuffer`, `full`, `linspace`, `logspace`, `ones`, or `zeros` functions.
|
||||
|
||||
`ndarray`s can be sliced, and iterated on, and have a number of their own methods, and properties, such as `flatten()`, `itemsize`, `reshape()`,
|
||||
`shape`, `size`, `strides`, `tobytes()`, `tolist()`, and `transpose()` and `T`. If the firmware is compiled with `complex` support,
|
||||
the `imag`, and `real` properties are automatically included.
|
||||
|
||||
## `numpy` and `scipy` functions
|
||||
|
||||
In addition, `ulab` includes [universal functions](https://micropython-ulab.readthedocs.io/en/latest/numpy-universal.html), [many `numpy` functions](https://micropython-ulab.readthedocs.io/en/latest/numpy-functions.html), and functions from the [`numpy.fft`](https://micropython-ulab.readthedocs.io/en/latest/numpy-fft.html), [`numpy.linalg`](https://micropython-ulab.readthedocs.io/en/latest/numpy-linalg.html), [`numpy.random`](https://micropython-ulab.readthedocs.io/en/latest/numpy-random.html), [`scipy.linalg`](https://micropython-ulab.readthedocs.io/en/latest/scipy-linalg.html), [`scipy.optimize`](https://micropython-ulab.readthedocs.io/en/latest/scipy-optimize.html), [`scipy.signal`](https://micropython-ulab.readthedocs.io/en/latest/scipy-signal.html), and [`scipy.special`](https://micropython-ulab.readthedocs.io/en/latest/scipy-special.html) modules. A complete list of available routines can be found under [micropython-ulab](https://micropython-ulab.readthedocs.io/en/latest).
|
||||
|
||||
## `ulab` utilities
|
||||
|
||||
The [`utils`](https://micropython-ulab.readthedocs.io/en/latest/ulab-utils.html) module contains functions for
|
||||
interfacing with peripheral devices supporting the buffer protocol. These functions do not have an obvious
|
||||
`numpy` equivalent, but share a similar programming interface, and allow direct data input-output between
|
||||
numerical arrays and hardware components.
|
||||
|
||||
## `user` module
|
||||
|
||||
User-defined functions operating on numerical data can easily be added via the `user` module. This allows for transparent extensions, without having to change anything in the core. Hints as to how to work with `ndarray`s at the C level can be found in the [programming manual](https://micropython-ulab.readthedocs.io/en/latest/ulab-programming.html).
|
||||
|
||||
# Usage
|
||||
|
||||
`ulab` sports a `numpy/scipy`-compatible interface, which makes porting of `CPython` code straightforward. The following
|
||||
snippet should run equally well in `micropython`, or on a PC.
|
||||
|
||||
```python
|
||||
try:
|
||||
from ulab import numpy
|
||||
from ulab import scipy
|
||||
except ImportError:
|
||||
import numpy
|
||||
import scipy.special
|
||||
|
||||
x = numpy.array([1, 2, 3])
|
||||
scipy.special.erf(x)
|
||||
```
|
||||
|
||||
# Finding help
|
||||
|
||||
Documentation can be found on [readthedocs](https://readthedocs.org/) under
|
||||
[micropython-ulab](https://micropython-ulab.readthedocs.io/en/latest),
|
||||
as well as at [circuitpython-ulab](https://circuitpython.readthedocs.io/en/latest/shared-bindings/ulab/__init__.html).
|
||||
A number of practical examples are listed in Jeff Epler's excellent
|
||||
[circuitpython-ulab](https://learn.adafruit.com/ulab-crunch-numbers-fast-with-circuitpython/overview) overview.
|
||||
The [tricks](https://micropython-ulab.readthedocs.io/en/latest/ulab-tricks.html) chapter of the user manual discusses
|
||||
methods by which RAM and speed can be leveraged in particular numerical problems.
|
||||
|
||||
# Benchmarks
|
||||
|
||||
Representative numbers on performance can be found under [ulab samples](https://github.com/thiagofe/ulab_samples).
|
||||
|
||||
# Firmware
|
||||
|
||||
Pre-built, and up-to-date firmware files for select platforms can be downloaded
|
||||
from [micropython-builder](https://github.com/v923z/micropython-builder).
|
||||
## Customising the firmware
|
||||
|
||||
If flash space is a concern, unnecessary functions can be excluded from the compiled firmware with
|
||||
pre-processor switches. In addition, `ulab` also has options for trading execution speed for firmware size.
|
||||
A thorough discussion on how the firmware can be customised can be found in the
|
||||
[corresponding section](https://micropython-ulab.readthedocs.io/en/latest/ulab-intro.html#customising-the-firmware)
|
||||
of the user manual.
|
||||
|
||||
## Platforms including ulab
|
||||
|
||||
`ulab` is also included in the following compiled `micropython` variants and derivatives:
|
||||
|
||||
1. `CircuitPython` for SAMD51 and nRF microcontrollers https://github.com/adafruit/circuitpython
|
||||
1. `MicroPython for K210` https://github.com/loboris/MicroPython_K210_LoBo
|
||||
1. `MaixPy` https://github.com/sipeed/MaixPy
|
||||
1. `OpenMV` https://github.com/openmv/openmv
|
||||
1. `pimoroni-pico` https://github.com/pimoroni/pimoroni-pico
|
||||
1. `Tulip Creative Computer` https://github.com/shorepine/tulipcc
|
||||
|
||||
## Compiling
|
||||
|
||||
If you want to try the latest version of `ulab` on `micropython` or one of its forks, the firmware can be compiled
|
||||
from the source by following these steps:
|
||||
|
||||
### UNIX port
|
||||
|
||||
Simply clone the `ulab` repository with
|
||||
|
||||
```bash
|
||||
git clone https://github.com/v923z/micropython-ulab.git ulab
|
||||
```
|
||||
and then run
|
||||
|
||||
```bash
|
||||
./build.sh [matrix.dims] # Dimensions is 2 by default
|
||||
```
|
||||
This command will clone `micropython`, and build the `unix` port automatically, as well as run the test scripts. If you want an interactive `unix` session, you can launch it in
|
||||
|
||||
```bash
|
||||
ulab/micropython/ports/unix
|
||||
```
|
||||
|
||||
### STM-based boards
|
||||
|
||||
First, you have to clone the `micropython` repository by running
|
||||
|
||||
```bash
|
||||
git clone https://github.com/micropython/micropython.git
|
||||
```
|
||||
on the command line. This will create a new repository with the name `micropython`. Staying there, clone the `ulab` repository with
|
||||
|
||||
```bash
|
||||
git clone https://github.com/v923z/micropython-ulab.git ulab
|
||||
```
|
||||
If you don't have the cross-compiler installed, your might want to do that now, for instance on Linux by executing
|
||||
|
||||
```bash
|
||||
sudo apt-get install gcc-arm-none-eabi
|
||||
```
|
||||
|
||||
If this step was successful, you can try to run the `make` command in the port's directory as
|
||||
|
||||
```bash
|
||||
make BOARD=PYBV11 USER_C_MODULES=../../../ulab all
|
||||
```
|
||||
which will prepare the firmware for pyboard.v.11. Similarly,
|
||||
|
||||
```bash
|
||||
make BOARD=PYBD_SF6 USER_C_MODULES=../../../ulab all
|
||||
```
|
||||
will compile for the SF6 member of the PYBD series. If your target is `unix`, you don't need to specify the `BOARD` parameter.
|
||||
|
||||
Provided that you managed to compile the firmware, you would upload that by running either
|
||||
|
||||
```bash
|
||||
dfu-util --alt 0 -D firmware.dfu
|
||||
```
|
||||
or
|
||||
|
||||
```bash
|
||||
python pydfu.py -u firmware.dfu
|
||||
```
|
||||
|
||||
In case you got stuck somewhere in the process, a bit more detailed instructions can be found under https://github.com/micropython/micropython/wiki/Getting-Started, and https://github.com/micropython/micropython/wiki/Pyboard-Firmware-Update.
|
||||
|
||||
|
||||
### ESP32-based boards
|
||||
|
||||
`ulab` can be tested on the ESP32 in [wokwi's micropython emulator](https://wokwi.com/arduino/projects/322114140704342610) without having to compile the C code. This utility also offers the possibility to save and share your `micropython` code.
|
||||
|
||||
Firmware for `Espressif` hardware can be built in two different ways, which are discussed in the next two paragraphs. A solution for issues with the firmware size is outlined in the [last paragraph](#what-to-do-if-the-firmware-is-too-large) of this section.
|
||||
|
||||
#### Compiling with cmake
|
||||
|
||||
Beginning with version 1.15, `micropython` switched to `cmake` on the ESP32 port. If your operating system supports `CMake > 3.12`, you can either simply download, and run the single [build script](https://github.com/v923z/micropython-ulab/blob/master/build/esp32-cmake.sh), or follow the step in this section. Otherwise, you should skip to the [next one](#compiling-with-make), where the old, `make`-based approach is discussed.
|
||||
|
||||
In case you encounter difficulties during the build process, you can consult the (general instructions for the ESP32)[https://github.com/micropython/micropython/tree/master/ports/esp32#micropython-port-to-the-esp32].
|
||||
|
||||
First, clone the `ulab`, the `micropython`, as well as the `espressif` repositories:
|
||||
|
||||
```bash
|
||||
export BUILD_DIR=$(pwd)
|
||||
|
||||
git clone https://github.com/v923z/micropython-ulab.git ulab
|
||||
git clone https://github.com/micropython/micropython.git
|
||||
|
||||
cd $BUILD_DIR/micropython/
|
||||
|
||||
git clone -b v4.0.2 --recursive https://github.com/espressif/esp-idf.git
|
||||
|
||||
```
|
||||
Also later releases of `esp-idf` are possible (e.g. `v4.2.1`).
|
||||
|
||||
Then install the `ESP-IDF` tools:
|
||||
|
||||
```bash
|
||||
cd esp-idf
|
||||
./install.sh
|
||||
. ./export.sh
|
||||
```
|
||||
|
||||
Next, build the `micropython` cross-compiler, and the `ESP` sub-modules:
|
||||
|
||||
```bash
|
||||
cd $BUILD_DIR/micropython/mpy-cross
|
||||
make
|
||||
cd $BUILD_DIR/micropython/ports/esp32
|
||||
make submodules
|
||||
```
|
||||
At this point, all requirements are installed and built. We can now compile the firmware with `ulab`. In `$BUILD_DIR/micropython/ports/esp32` create a `makefile` with the following content:
|
||||
|
||||
```bash
|
||||
BOARD = GENERIC
|
||||
USER_C_MODULES = $(BUILD_DIR)/ulab/code/micropython.cmake
|
||||
|
||||
include Makefile
|
||||
```
|
||||
You specify with the `BOARD` variable, what you want to compile for, a generic board, or `TINYPICO` (for `micropython` version >1.1.5, use `UM_TINYPICO`), etc. Still in `$BUILD_DIR/micropython/ports/esp32`, you can now run `make`.
|
||||
|
||||
#### Compiling with make
|
||||
|
||||
If your operating system does not support a recent enough version of `CMake`, you have to stay with `micropython` version 1.14. The firmware can be compiled either by downloading and running the [build script](https://github.com/v923z/micropython-ulab/blob/master/build/esp32.sh), or following the steps below:
|
||||
|
||||
First, clone `ulab` with
|
||||
|
||||
```bash
|
||||
git clone https://github.com/v923z/micropython-ulab.git ulab
|
||||
```
|
||||
|
||||
and then, in the same directory, `micropython`
|
||||
|
||||
```bash
|
||||
git clone https://github.com/micropython/micropython.git
|
||||
```
|
||||
|
||||
At this point, you should have `ulab`, and `micropython` side by side.
|
||||
|
||||
With version 1.14, `micropython` switched to `cmake` on the `ESP32` port, thus breaking compatibility with user modules. `ulab` can, however, still be compiled with version 1.14. You can check out a particular version by pinning the release tag as
|
||||
|
||||
```bash
|
||||
|
||||
cd ./micropython/
|
||||
git checkout tags/v1.14
|
||||
|
||||
```
|
||||
Next, update the submodules,
|
||||
|
||||
```bash
|
||||
git submodule update --init
|
||||
cd ./mpy-cross && make # build cross-compiler (required)
|
||||
```
|
||||
and find the ESP commit hash
|
||||
|
||||
```bash
|
||||
cd ./micropython/ports/esp32
|
||||
make ESPIDF= # will display supported ESP-IDF commit hashes
|
||||
# output should look like: """
|
||||
# ...
|
||||
# Supported git hash (v3.3): 9e70825d1e1cbf7988cf36981774300066580ea7
|
||||
# Supported git hash (v4.0) (experimental): 4c81978a3e2220674a432a588292a4c860eef27b
|
||||
```
|
||||
|
||||
Choose an ESPIDF version from one of the options printed by the previous command:
|
||||
|
||||
```bash
|
||||
ESPIDF_VER=9e70825d1e1cbf7988cf36981774300066580ea7
|
||||
```
|
||||
|
||||
In the `micropython` directory, create a new directory with
|
||||
```bash
|
||||
mkdir esp32
|
||||
```
|
||||
Your `micropython` directory should now look like
|
||||
|
||||
```bash
|
||||
ls
|
||||
ACKNOWLEDGEMENTS CONTRIBUTING.md esp32 lib mpy-cross README.md
|
||||
CODECONVENTIONS.md docs examples LICENSE ports tests
|
||||
CODEOFCONDUCT.md drivers extmod logo py tools
|
||||
```
|
||||
|
||||
In `./micropython/esp32`, download the software development kit with
|
||||
|
||||
```bash
|
||||
git clone https://github.com/espressif/esp-idf.git esp-idf
|
||||
cd ./esp-idf
|
||||
git checkout $ESPIDF_VER
|
||||
git submodule update --init --recursive # get idf submodules
|
||||
pip install -r ./requirements.txt # install python reqs
|
||||
```
|
||||
|
||||
Next, still staying in `./micropython/eps32/esd-idf/`, install the ESP32 compiler. If using an ESP-IDF version >= 4.x (chosen by `$ESPIDF_VER` above), this can be done by running `. $BUILD_DIR/esp-idf/install.sh`. Otherwise, for version 3.x, run the following commands in in `.micropython/esp32/esp-idf`:
|
||||
|
||||
```bash
|
||||
# for 64 bit linux
|
||||
curl https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz | tar xvz
|
||||
|
||||
# for 32 bit
|
||||
# curl https://dl.espressif.com/dl/xtensa-esp32-elf-linux32-1.22.0-80-g6c4433a-5.2.0.tar.gz | tar xvz
|
||||
|
||||
# don't worry about adding to path; we'll specify that later
|
||||
|
||||
# also, see https://docs.espressif.com/projects/esp-idf/en/v3.3.2/get-started for more info
|
||||
```
|
||||
|
||||
Finally, build the firmware:
|
||||
|
||||
```bash
|
||||
cd ./micropython/ports/esp32
|
||||
# temporarily add esp32 compiler to path
|
||||
export PATH=../../esp32/esp-idf/xtensa-esp32-elf/bin:$PATH
|
||||
export ESPIDF=../../esp32/esp-idf # req'd by Makefile
|
||||
export BOARD=GENERIC # options are dirs in ./boards
|
||||
export USER_C_MODULES=../../../ulab # include ulab in firmware
|
||||
|
||||
make submodules & make all
|
||||
```
|
||||
|
||||
If it compiles without error, you can plug in your ESP32 via USB and then flash it with:
|
||||
|
||||
```bash
|
||||
make erase && make deploy
|
||||
```
|
||||
|
||||
#### What to do, if the firmware is too large?
|
||||
|
||||
When selecting `BOARD=TINYPICO`, the firmware is built but fails to deploy, because it is too large for the standard partitions. We can rectify the problem by creating a new partition table. In order to do so, in `$BUILD_DIR/micropython/ports/esp32/`, copy the following 8 lines to a file named `partitions_ulab.cvs`:
|
||||
|
||||
```
|
||||
# Notes: the offset of the partition table itself is set in
|
||||
# $ESPIDF/components/partition_table/Kconfig.projbuild and the
|
||||
# offset of the factory/ota_0 partition is set in makeimg.py
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
phy_init, data, phy, 0xf000, 0x1000,
|
||||
factory, app, factory, 0x10000, 0x200000,
|
||||
vfs, data, fat, 0x220000, 0x180000,
|
||||
```
|
||||
This expands the `factory` partition by 128 kB, and reduces the size of `vfs` by the same amount. Having defined the new partition table, we should extend `sdkconfig.board` by adding the following two lines:
|
||||
|
||||
```
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_ulab.csv"
|
||||
```
|
||||
This file can be found in `$BUILD_DIR/micropython/ports/esp32/boards/TINYPICO/`. Finally, run `make clean`, and `make`. The new firmware contains the modified partition table, and should fit on the microcontroller.
|
||||
|
||||
### RP2-based boards
|
||||
|
||||
RP2 firmware can be compiled either by downloading and running the single [build script](https://github.com/v923z/micropython-ulab/blob/master/build/rp2.sh)/[build script for Pico W](https://github.com/v923z/micropython-ulab/blob/master/build/rp2w.sh), or executing the commands below.
|
||||
|
||||
First, clone `micropython`:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/micropython/micropython.git
|
||||
```
|
||||
|
||||
Then, setup the required submodules:
|
||||
|
||||
```bash
|
||||
cd micropython
|
||||
git submodule update --init lib/tinyusb
|
||||
git submodule update --init lib/pico-sdk
|
||||
cd lib/pico-sdk
|
||||
git submodule update --init lib/tinyusb
|
||||
```
|
||||
|
||||
You'll also need to compile `mpy-cross`:
|
||||
|
||||
```bash
|
||||
cd ../../mpy-cross
|
||||
make
|
||||
```
|
||||
|
||||
That's all you need to do for the `micropython` repository. Now, let us clone `ulab` (in a directory outside the micropython repository):
|
||||
|
||||
```bash
|
||||
cd ../../
|
||||
git clone https://github.com/v923z/micropython-ulab ulab
|
||||
```
|
||||
|
||||
With this setup, we can now build the firmware. Back in the `micropython` repository, use these commands:
|
||||
|
||||
```bash
|
||||
cd ports/rp2
|
||||
make USER_C_MODULES=/path/to/ulab/code/micropython.cmake
|
||||
```
|
||||
|
||||
If `micropython` and `ulab` were in the same folder on the computer, you can set `USER_C_MODULES=../../../ulab/code/micropython.cmake`. The compiled firmware will be placed in `micropython/ports/rp2/build`.
|
||||
|
||||
# Compiling for CircuitPython
|
||||
|
||||
[Adafruit Industries](www.adafruit.com) always include a relatively recent version of `ulab` in their nightly builds. However, if you really need the bleeding edge, you can easily compile the firmware from the source. Simply clone `circuitpython`, and move the commit pointer to the latest version of `ulab` (`ulab` will automatically be cloned with `circuitpython`):
|
||||
|
||||
```bash
|
||||
git clone https://github.com/adafruit/circuitpython.git
|
||||
|
||||
cd circuitpyton/extmod/ulab
|
||||
|
||||
# update ulab here
|
||||
git checkout master
|
||||
git pull
|
||||
```
|
||||
You might have to check, whether the `CIRCUITPY_ULAB` variable is set to `1` for the port that you want to compile for. You find this piece of information in the `make` fragment:
|
||||
|
||||
```bash
|
||||
circuitpython/ports/port_of_your_choice/mpconfigport.mk
|
||||
```
|
||||
After this, you would run `make` with the single `BOARD` argument, e.g.:
|
||||
|
||||
```bash
|
||||
make BOARD=mini_sam_m4
|
||||
```
|
||||
|
||||
# Issues, contributing, and testing
|
||||
|
||||
If you find a problem with the code, please, raise an [issue](https://github.com/v923z/micropython-ulab/issues)! An issue should address a single problem, and should contain a minimal code snippet that demonstrates the difference from the expected behaviour. Reducing a problem to the bare minimum significantly increases the chances of a quick fix.
|
||||
|
||||
Feature requests (porting a particular function from `numpy` or `scipy`) should also be posted at [ulab issue](https://github.com/v923z/micropython-ulab/issues).
|
||||
|
||||
Contributions of any kind are always welcome. If you feel like adding to the code, you can simply issue a pull request. If you do so, please, try to adhere to `micropython`'s [coding conventions](https://github.com/micropython/micropython/blob/master/CODECONVENTIONS.md#c-code-conventions).
|
||||
|
||||
However, you can also contribute to the documentation (preferably via the [jupyter notebooks](https://github.com/v923z/micropython-ulab/tree/master/docs), or improve the [tests](https://github.com/v923z/micropython-ulab/tree/master/tests).
|
||||
|
||||
## Testing
|
||||
|
||||
If you decide to lend a hand with testing, here are the steps:
|
||||
|
||||
1. Write a test script that checks a particular function, or a set of related functions!
|
||||
1. Drop this script in one of the folders in [ulab tests](https://github.com/v923z/micropython-ulab/tree/master/tests)!
|
||||
1. Run the [./build.sh](https://github.com/v923z/micropython-ulab/blob/master/build.sh) script in the root directory of `ulab`! This will clone the latest `micropython`, compile the firmware for `unix`, execute all scripts in the `ulab/tests`, and compare the results to those in the expected results files, which are also in `ulab/tests`, and have an extension `.exp`. In case you have a new snippet, i.e., you have no expected results file, or if the results differ from those in the expected file, a new expected file will be generated in the root directory. You should inspect the contents of this file, and if they are satisfactory, then the file can be moved to the `ulab/tests` folder, alongside your snippet.
|
||||
|
|
|
|||
52
build-cp.sh
Executable file
52
build-cp.sh
Executable file
|
|
@ -0,0 +1,52 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
# POSIX compliant version
|
||||
readlinkf_posix() {
|
||||
[ "${1:-}" ] || return 1
|
||||
max_symlinks=40
|
||||
CDPATH='' # to avoid changing to an unexpected directory
|
||||
|
||||
target=$1
|
||||
[ -e "${target%/}" ] || target=${1%"${1##*[!/]}"} # trim trailing slashes
|
||||
[ -d "${target:-/}" ] && target="$target/"
|
||||
|
||||
cd -P . 2>/dev/null || return 1
|
||||
while [ "$max_symlinks" -ge 0 ] && max_symlinks=$((max_symlinks - 1)); do
|
||||
if [ ! "$target" = "${target%/*}" ]; then
|
||||
case $target in
|
||||
/*) cd -P "${target%/*}/" 2>/dev/null || break ;;
|
||||
*) cd -P "./${target%/*}" 2>/dev/null || break ;;
|
||||
esac
|
||||
target=${target##*/}
|
||||
fi
|
||||
|
||||
if [ ! -L "$target" ]; then
|
||||
target="${PWD%/}${target:+/}${target}"
|
||||
printf '%s\n' "${target:-/}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# `ls -dl` format: "%s %u %s %s %u %s %s -> %s\n",
|
||||
# <file mode>, <number of links>, <owner name>, <group name>,
|
||||
# <size>, <date and time>, <pathname of link>, <contents of link>
|
||||
# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html
|
||||
link=$(ls -dl -- "$target" 2>/dev/null) || break
|
||||
target=${link#*" $target -> "}
|
||||
done
|
||||
return 1
|
||||
}
|
||||
NPROC=$(python3 -c 'import multiprocessing; print(multiprocessing.cpu_count())')
|
||||
HERE="$(dirname -- "$(readlinkf_posix -- "${0}")" )"
|
||||
[ -e circuitpython/py/py.mk ] || (git clone --branch main https://github.com/adafruit/circuitpython && cd circuitpython && make fetch-all-submodules && git submodule update --init lib/uzlib tools)
|
||||
rm -rf circuitpython/extmod/ulab; ln -s "$HERE" circuitpython/extmod/ulab
|
||||
dims=${1-2}
|
||||
make -C circuitpython/mpy-cross -j$NPROC
|
||||
make -k -C circuitpython/ports/unix -j$NPROC DEBUG=1 MICROPY_PY_FFI=0 MICROPY_PY_BTREE=0 MICROPY_SSL_AXTLS=0 MICROPY_PY_USSL=0 CFLAGS_EXTRA="-Wno-tautological-constant-out-of-range-compare -Wno-unknown-pragmas -DULAB_MAX_DIMS=$dims" BUILD=build-$dims PROG=micropython-$dims
|
||||
|
||||
# bash test-common.sh "${dims}" "circuitpython/ports/unix/micropython-$dims"
|
||||
|
||||
# Docs don't depend on the dimensionality, so only do it once
|
||||
if [ "$dims" -eq 2 ]; then
|
||||
(cd circuitpython && sphinx-build -E -W -b html . _build/html)
|
||||
(cd circuitpython && make check-stubs)
|
||||
fi
|
||||
70
build.sh
Executable file
70
build.sh
Executable file
|
|
@ -0,0 +1,70 @@
|
|||
#!/bin/sh
|
||||
|
||||
GIT_HASH=`git describe --abbrev=8 --always`
|
||||
|
||||
# POSIX compliant version
|
||||
readlinkf_posix() {
|
||||
[ "${1:-}" ] || return 1
|
||||
max_symlinks=40
|
||||
CDPATH='' # to avoid changing to an unexpected directory
|
||||
|
||||
target=$1
|
||||
[ -e "${target%/}" ] || target=${1%"${1##*[!/]}"} # trim trailing slashes
|
||||
[ -d "${target:-/}" ] && target="$target/"
|
||||
|
||||
cd -P . 2>/dev/null || return 1
|
||||
while [ "$max_symlinks" -ge 0 ] && max_symlinks=$((max_symlinks - 1)); do
|
||||
if [ ! "$target" = "${target%/*}" ]; then
|
||||
case $target in
|
||||
/*) cd -P "${target%/*}/" 2>/dev/null || break ;;
|
||||
*) cd -P "./${target%/*}" 2>/dev/null || break ;;
|
||||
esac
|
||||
target=${target##*/}
|
||||
fi
|
||||
|
||||
if [ ! -L "$target" ]; then
|
||||
target="${PWD%/}${target:+/}${target}"
|
||||
printf '%s\n' "${target:-/}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# `ls -dl` format: "%s %u %s %s %u %s %s -> %s\n",
|
||||
# <file mode>, <number of links>, <owner name>, <group name>,
|
||||
# <size>, <date and time>, <pathname of link>, <contents of link>
|
||||
# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html
|
||||
link=$(ls -dl -- "$target" 2>/dev/null) || break
|
||||
target=${link#*" $target -> "}
|
||||
done
|
||||
return 1
|
||||
}
|
||||
NPROC=`python3 -c 'import multiprocessing; print(multiprocessing.cpu_count())'`
|
||||
PLATFORM=`python3 -c 'import sys; print(sys.platform)'`
|
||||
set -e
|
||||
HERE="$(dirname -- "$(readlinkf_posix -- "${0}")" )"
|
||||
dims=${1-2}
|
||||
if [ ! -d "micropython" ] ; then
|
||||
git clone https://github.com/micropython/micropython
|
||||
else
|
||||
git -C micropython pull
|
||||
fi
|
||||
make -C micropython/mpy-cross -j${NPROC}
|
||||
make -C micropython/ports/unix submodules
|
||||
make -C micropython/ports/unix -j${NPROC} USER_C_MODULES="${HERE}" DEBUG=1 STRIP=: MICROPY_PY_FFI=0 MICROPY_PY_BTREE=0 CFLAGS_EXTRA=-DULAB_MAX_DIMS=$dims CFLAGS_EXTRA+=-DULAB_HASH=$GIT_HASH BUILD=build-$dims PROG=micropython-$dims
|
||||
|
||||
PROG="micropython/ports/unix/build-$dims/micropython-$dims"
|
||||
if [ ! -e "$PROG" ]; then
|
||||
# Older MicroPython revision, executable is still in ports/unix.
|
||||
PROG="micropython/ports/unix/micropython-$dims"
|
||||
fi
|
||||
|
||||
bash test-common.sh "${dims}" "$PROG"
|
||||
|
||||
# Build with single-precision float.
|
||||
make -C micropython/ports/unix -j${NPROC} USER_C_MODULES="${HERE}" DEBUG=1 STRIP=: MICROPY_PY_FFI=0 MICROPY_PY_BTREE=0 CFLAGS_EXTRA=-DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_FLOAT CFLAGS_EXTRA+=-DULAB_MAX_DIMS=$dims CFLAGS_EXTRA+=-DULAB_HASH=$GIT_HASH BUILD=build-nanbox-$dims PROG=micropython-nanbox-$dims
|
||||
|
||||
# The unix nanbox variant builds as a 32-bit executable and requires gcc-multilib.
|
||||
# macOS doesn't support i386 builds so only build on linux.
|
||||
if [ $PLATFORM = linux ]; then
|
||||
make -C micropython/ports/unix -j${NPROC} VARIANT=nanbox USER_C_MODULES="${HERE}" DEBUG=1 STRIP=: MICROPY_PY_FFI=0 MICROPY_PY_BTREE=0 CFLAGS_EXTRA=-DULAB_MAX_DIMS=$dims CFLAGS_EXTRA+=-DULAB_HASH=$GIT_HASH BUILD=build-nanbox-$dims PROG=micropython-nanbox-$dims
|
||||
fi
|
||||
|
||||
35
build/esp32-cmake.sh
Normal file
35
build/esp32-cmake.sh
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#!/bin/bash
|
||||
|
||||
export BUILD_DIR=$(pwd)
|
||||
|
||||
echo "--- CLONING ULAB ---"
|
||||
git clone --depth 1 https://github.com/v923z/micropython-ulab.git ulab
|
||||
|
||||
echo "--- CLONING MICROPYTHON ---"
|
||||
git clone --depth 1 https://github.com/micropython/micropython.git
|
||||
|
||||
echo "--- CLONING ESP-IDF ---"
|
||||
cd $BUILD_DIR/micropython/
|
||||
git clone --depth 1 -b v4.0.2 --recursive https://github.com/espressif/esp-idf.git
|
||||
|
||||
echo "--- INSTALL ESP-IDF ---"
|
||||
cd $BUILD_DIR/micropython/esp-idf
|
||||
./install.sh
|
||||
. ./export.sh
|
||||
|
||||
echo "--- MPY-CROSS ---"
|
||||
cd $BUILD_DIR/micropython/mpy-cross
|
||||
make
|
||||
|
||||
echo "--- ESP32 SUBMODULES ---"
|
||||
cd $BUILD_DIR/micropython/ports/esp32
|
||||
make submodules
|
||||
|
||||
echo "--- PATCH MAKEFILE ---"
|
||||
cp $BUILD_DIR/micropython/ports/esp32/Makefile $BUILD_DIR/micropython/ports/esp32/MakefileOld
|
||||
echo "BOARD = GENERIC" > $BUILD_DIR/micropython/ports/esp32/Makefile
|
||||
echo "USER_C_MODULES = \$(BUILD_DIR)/ulab/code/micropython.cmake" >> $BUILD_DIR/micropython/ports/esp32/Makefile
|
||||
cat $BUILD_DIR/micropython/ports/esp32/MakefileOld >> $BUILD_DIR/micropython/ports/esp32/Makefile
|
||||
|
||||
echo "--- MAKE ---"
|
||||
make
|
||||
41
build/esp32.sh
Normal file
41
build/esp32.sh
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#!/bin/bash
|
||||
|
||||
export BUILD_DIR=$(pwd)
|
||||
|
||||
git clone https://github.com/v923z/micropython-ulab.git ulab
|
||||
git clone https://github.com/micropython/micropython.git
|
||||
|
||||
cd $BUILD_DIR/micropython/
|
||||
git checkout tags/v1.14
|
||||
|
||||
git submodule update --init
|
||||
cd ./mpy-cross && make # build cross-compiler (required)
|
||||
|
||||
cd $BUILD_DIR/micropython/ports/esp32
|
||||
make ESPIDF= # will display supported ESP-IDF commit hashes
|
||||
# output should look like: """
|
||||
# ...
|
||||
# Supported git hash (v3.3): 9e70825d1e1cbf7988cf36981774300066580ea7
|
||||
# Supported git hash (v4.0) (experimental): 4c81978a3e2220674a432a588292a4c860eef27b
|
||||
|
||||
ESPIDF_VER=9e70825d1e1cbf7988cf36981774300066580ea7
|
||||
|
||||
mkdir $BUILD_DIR/micropython/esp32
|
||||
|
||||
cd $BUILD_DIR/micropython/esp32
|
||||
git clone https://github.com/espressif/esp-idf.git esp-idf
|
||||
cd $BUILD_DIR/micropython/esp32/esp-idf
|
||||
git checkout $ESPIDF_VER
|
||||
git submodule update --init --recursive # get idf submodules
|
||||
pip install -r ./requirements.txt # install python reqs
|
||||
|
||||
curl https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz | tar xvz
|
||||
|
||||
cd $BUILD_DIR/micropython/ports/esp32
|
||||
# temporarily add esp32 compiler to path
|
||||
export PATH=$BUILD_DIR/micropython/esp32/esp-idf/xtensa-esp32-elf/bine:$PATH
|
||||
export ESPIDF=$BUILD_DIR/micropython/esp32/esp-idf
|
||||
export BOARD=GENERIC # board options are in ./board
|
||||
export USER_C_MODULES=$BUILD_DIR/ulab # include ulab in firmware
|
||||
|
||||
make submodules & make all
|
||||
24
build/rp2.sh
Normal file
24
build/rp2.sh
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#!/bin/bash
|
||||
|
||||
export BUILD_DIR=$(pwd)
|
||||
export MPY_DIR=$BUILD_DIR/micropython
|
||||
export ULAB_DIR=$BUILD_DIR/../code
|
||||
|
||||
if [ ! -d $ULAB_DIR ]; then
|
||||
printf "Cloning ulab\n"
|
||||
ULAB_DIR=$BUILD_DIR/ulab/code
|
||||
git clone https://github.com/v923z/micropython-ulab.git ulab
|
||||
fi
|
||||
|
||||
if [ ! -d $MPY_DIR ]; then
|
||||
printf "Cloning MicroPython\n"
|
||||
git clone https://github.com/micropython/micropython.git micropython
|
||||
fi
|
||||
|
||||
cd $MPY_DIR
|
||||
git submodule update --init
|
||||
cd ./mpy-cross && make # build cross-compiler (required)
|
||||
|
||||
cd $MPY_DIR/ports/rp2
|
||||
rm -r build
|
||||
make USER_C_MODULES=$ULAB_DIR/micropython.cmake
|
||||
25
build/rp2w.sh
Normal file
25
build/rp2w.sh
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#!/bin/bash
|
||||
|
||||
export BOARD=RPI_PICO_W
|
||||
export BUILD_DIR=$(pwd)
|
||||
export MPY_DIR=$BUILD_DIR/micropython
|
||||
export ULAB_DIR=$BUILD_DIR/../code
|
||||
|
||||
if [ ! -d $ULAB_DIR ]; then
|
||||
printf "Cloning ulab\n"
|
||||
ULAB_DIR=$BUILD_DIR/ulab/code
|
||||
git clone https://github.com/v923z/micropython-ulab.git ulab
|
||||
fi
|
||||
|
||||
if [ ! -d $MPY_DIR ]; then
|
||||
printf "Cloning MicroPython\n"
|
||||
git clone https://github.com/micropython/micropython.git micropython
|
||||
fi
|
||||
|
||||
cd $MPY_DIR
|
||||
git submodule update --init
|
||||
cd ./mpy-cross && make # build cross-compiler (required)
|
||||
|
||||
cd $MPY_DIR/ports/rp2
|
||||
make BOARD=$BOARD clean
|
||||
make USER_C_MODULES=$ULAB_DIR/micropython.cmake BOARD=$BOARD
|
||||
169
code/fft.c
169
code/fft.c
|
|
@ -1,169 +0,0 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "py/runtime.h"
|
||||
#include "py/binary.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/objarray.h"
|
||||
#include "ndarray.h"
|
||||
#include "fft.h"
|
||||
|
||||
|
||||
enum FFT_TYPE {
|
||||
FFT_FFT,
|
||||
FFT_IFFT,
|
||||
FFT_SPECTRUM,
|
||||
};
|
||||
|
||||
void fft_kernel(mp_float_t *real, mp_float_t *imag, int n, int isign) {
|
||||
// This is basically a modification of four1 from Numerical Recipes
|
||||
// The main difference is that this function takes two arrays, one
|
||||
// for the real, and one for the imaginary parts.
|
||||
int j, m, mmax, istep;
|
||||
mp_float_t tempr, tempi;
|
||||
mp_float_t wtemp, wr, wpr, wpi, wi, theta;
|
||||
|
||||
j = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
if (j > i) {
|
||||
SWAP(float, real[i], real[j]);
|
||||
SWAP(float, imag[i], imag[j]);
|
||||
}
|
||||
m = n >> 1;
|
||||
while (j >= m && m > 0) {
|
||||
j -= m;
|
||||
m >>= 1;
|
||||
}
|
||||
j += m;
|
||||
}
|
||||
|
||||
mmax = 1;
|
||||
while (n > mmax) {
|
||||
istep = mmax << 1;
|
||||
theta = -1.0*isign*6.28318530717959/istep;
|
||||
wtemp = MICROPY_FLOAT_C_FUN(sin)(0.5 * theta);
|
||||
wpr = -2.0 * wtemp * wtemp;
|
||||
wpi = MICROPY_FLOAT_C_FUN(sin)(theta);
|
||||
wr = 1.0;
|
||||
wi = 0.0;
|
||||
for(m = 0; m < mmax; m++) {
|
||||
for(int i = m; i < n; i += istep) {
|
||||
j = i + mmax;
|
||||
tempr = wr * real[j] - wi * imag[j];
|
||||
tempi = wr * imag[j] + wi * real[j];
|
||||
real[j] = real[i] - tempr;
|
||||
imag[j] = imag[i] - tempi;
|
||||
real[i] += tempr;
|
||||
imag[i] += tempi;
|
||||
}
|
||||
wtemp = wr;
|
||||
wr = wr*wpr - wi*wpi + wr;
|
||||
wi = wi*wpr + wtemp*wpi + wi;
|
||||
}
|
||||
mmax = istep;
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_t fft_fft_ifft_spectrum(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_im, uint8_t type) {
|
||||
if(!MP_OBJ_IS_TYPE(arg_re, &ulab_ndarray_type)) {
|
||||
mp_raise_NotImplementedError("FFT is defined for ndarrays only");
|
||||
}
|
||||
if(n_args == 2) {
|
||||
if(!MP_OBJ_IS_TYPE(arg_im, &ulab_ndarray_type)) {
|
||||
mp_raise_NotImplementedError("FFT is defined for ndarrays only");
|
||||
}
|
||||
}
|
||||
// Check if input is of length of power of 2
|
||||
ndarray_obj_t *re = MP_OBJ_TO_PTR(arg_re);
|
||||
uint16_t len = re->array->len;
|
||||
if((len & (len-1)) != 0) {
|
||||
mp_raise_ValueError("input array length must be power of 2");
|
||||
}
|
||||
|
||||
ndarray_obj_t *out_re = create_new_ndarray(1, len, NDARRAY_FLOAT);
|
||||
mp_float_t *data_re = (mp_float_t *)out_re->array->items;
|
||||
|
||||
if(re->array->typecode == NDARRAY_FLOAT) {
|
||||
// By treating this case separately, we can save a bit of time.
|
||||
// I don't know if it is worthwhile, though...
|
||||
memcpy((mp_float_t *)out_re->array->items, (mp_float_t *)re->array->items, re->bytes);
|
||||
} else {
|
||||
for(size_t i=0; i < len; i++) {
|
||||
data_re[i] = ndarray_get_float_value(re->array->items, re->array->typecode, i);
|
||||
}
|
||||
}
|
||||
ndarray_obj_t *out_im = create_new_ndarray(1, len, NDARRAY_FLOAT);
|
||||
mp_float_t *data_im = (mp_float_t *)out_im->array->items;
|
||||
|
||||
if(n_args == 2) {
|
||||
ndarray_obj_t *im = MP_OBJ_TO_PTR(arg_im);
|
||||
if (re->array->len != im->array->len) {
|
||||
mp_raise_ValueError("real and imaginary parts must be of equal length");
|
||||
}
|
||||
if(im->array->typecode == NDARRAY_FLOAT) {
|
||||
memcpy((mp_float_t *)out_im->array->items, (mp_float_t *)im->array->items, im->bytes);
|
||||
} else {
|
||||
for(size_t i=0; i < len; i++) {
|
||||
data_im[i] = ndarray_get_float_value(im->array->items, im->array->typecode, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
if((type == FFT_FFT) || (type == FFT_SPECTRUM)) {
|
||||
fft_kernel(data_re, data_im, len, 1);
|
||||
if(type == FFT_SPECTRUM) {
|
||||
for(size_t i=0; i < len; i++) {
|
||||
data_re[i] = MICROPY_FLOAT_C_FUN(sqrt)(data_re[i]*data_re[i] + data_im[i]*data_im[i]);
|
||||
}
|
||||
}
|
||||
} else { // inverse transform
|
||||
fft_kernel(data_re, data_im, len, -1);
|
||||
// TODO: numpy accepts the norm keyword argument
|
||||
for(size_t i=0; i < len; i++) {
|
||||
data_re[i] /= len;
|
||||
data_im[i] /= len;
|
||||
}
|
||||
}
|
||||
if(type == FFT_SPECTRUM) {
|
||||
return MP_OBJ_TO_PTR(out_re);
|
||||
} else {
|
||||
mp_obj_t tuple[2];
|
||||
tuple[0] = out_re;
|
||||
tuple[1] = out_im;
|
||||
return mp_obj_new_tuple(2, tuple);
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_t fft_fft(size_t n_args, const mp_obj_t *args) {
|
||||
if(n_args == 2) {
|
||||
return fft_fft_ifft_spectrum(n_args, args[0], args[1], FFT_FFT);
|
||||
} else {
|
||||
return fft_fft_ifft_spectrum(n_args, args[0], mp_const_none, FFT_FFT);
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_t fft_ifft(size_t n_args, const mp_obj_t *args) {
|
||||
if(n_args == 2) {
|
||||
return fft_fft_ifft_spectrum(n_args, args[0], args[1], FFT_IFFT);
|
||||
} else {
|
||||
return fft_fft_ifft_spectrum(n_args, args[0], mp_const_none, FFT_IFFT);
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_t fft_spectrum(size_t n_args, const mp_obj_t *args) {
|
||||
if(n_args == 2) {
|
||||
return fft_fft_ifft_spectrum(n_args, args[0], args[1], FFT_SPECTRUM);
|
||||
} else {
|
||||
return fft_fft_ifft_spectrum(n_args, args[0], mp_const_none, FFT_SPECTRUM);
|
||||
}
|
||||
}
|
||||
23
code/fft.h
23
code/fft.h
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _FFT_
|
||||
#define _FFT_
|
||||
|
||||
#ifndef MP_PI
|
||||
#define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846)
|
||||
#endif
|
||||
|
||||
#define SWAP(t, a, b) { t tmp = a; a = b; b = tmp; }
|
||||
|
||||
mp_obj_t fft_fft(size_t , const mp_obj_t *);
|
||||
mp_obj_t fft_ifft(size_t , const mp_obj_t *);
|
||||
mp_obj_t fft_spectrum(size_t , const mp_obj_t *);
|
||||
#endif
|
||||
457
code/linalg.c
457
code/linalg.c
|
|
@ -1,457 +0,0 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
#include "linalg.h"
|
||||
|
||||
mp_obj_t linalg_transpose(mp_obj_t self_in) {
|
||||
ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
// the size of a single item in the array
|
||||
uint8_t _sizeof = mp_binary_get_size('@', self->array->typecode, NULL);
|
||||
|
||||
// NOTE:
|
||||
// if the matrices are square, we can simply swap items, but
|
||||
// generic matrices can't be transposed in place, so we have to
|
||||
// declare a temporary variable
|
||||
|
||||
// NOTE:
|
||||
// In the old matrix, the coordinate (m, n) is m*self->n + n
|
||||
// We have to assign this to the coordinate (n, m) in the new
|
||||
// matrix, i.e., to n*self->m + m (since the new matrix has self->m columns)
|
||||
|
||||
// one-dimensional arrays can be transposed by simply swapping the dimensions
|
||||
if((self->m != 1) && (self->n != 1)) {
|
||||
uint8_t *c = (uint8_t *)self->array->items;
|
||||
// self->bytes is the size of the bytearray, irrespective of the typecode
|
||||
uint8_t *tmp = m_new(uint8_t, self->bytes);
|
||||
for(size_t m=0; m < self->m; m++) {
|
||||
for(size_t n=0; n < self->n; n++) {
|
||||
memcpy(tmp+_sizeof*(n*self->m + m), c+_sizeof*(m*self->n + n), _sizeof);
|
||||
}
|
||||
}
|
||||
memcpy(self->array->items, tmp, self->bytes);
|
||||
m_del(uint8_t, tmp, self->bytes);
|
||||
}
|
||||
SWAP(size_t, self->m, self->n);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t linalg_reshape(mp_obj_t self_in, mp_obj_t shape) {
|
||||
ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if(!MP_OBJ_IS_TYPE(shape, &mp_type_tuple) || (MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(shape)) != 2)) {
|
||||
mp_raise_ValueError("shape must be a 2-tuple");
|
||||
}
|
||||
|
||||
mp_obj_iter_buf_t iter_buf;
|
||||
mp_obj_t item, iterable = mp_getiter(shape, &iter_buf);
|
||||
uint16_t m, n;
|
||||
item = mp_iternext(iterable);
|
||||
m = mp_obj_get_int(item);
|
||||
item = mp_iternext(iterable);
|
||||
n = mp_obj_get_int(item);
|
||||
if(m*n != self->m*self->n) {
|
||||
// TODO: the proper error message would be "cannot reshape array of size %d into shape (%d, %d)"
|
||||
mp_raise_ValueError("cannot reshape array (incompatible input/output shape)");
|
||||
}
|
||||
self->m = m;
|
||||
self->n = n;
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
}
|
||||
|
||||
mp_obj_t linalg_size(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj) } },
|
||||
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(1, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError("size is defined for ndarrays only");
|
||||
} else {
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
if(args[1].u_obj == mp_const_none) {
|
||||
return mp_obj_new_int(ndarray->array->len);
|
||||
} else if(mp_obj_is_int(args[1].u_obj)) {
|
||||
uint8_t ax = mp_obj_get_int(args[1].u_obj);
|
||||
if(ax == 0) {
|
||||
if(ndarray->m == 1) {
|
||||
return mp_obj_new_int(ndarray->n);
|
||||
} else {
|
||||
return mp_obj_new_int(ndarray->m);
|
||||
}
|
||||
} else if(ax == 1) {
|
||||
if(ndarray->m == 1) {
|
||||
mp_raise_ValueError("tuple index out of range");
|
||||
} else {
|
||||
return mp_obj_new_int(ndarray->n);
|
||||
}
|
||||
} else {
|
||||
mp_raise_ValueError("tuple index out of range");
|
||||
}
|
||||
} else {
|
||||
mp_raise_TypeError("wrong argument type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool linalg_invert_matrix(mp_float_t *data, size_t N) {
|
||||
// returns true, of the inversion was successful,
|
||||
// false, if the matrix is singular
|
||||
|
||||
// initially, this is the unit matrix: the contents of this matrix is what
|
||||
// will be returned after all the transformations
|
||||
mp_float_t *unit = m_new(mp_float_t, N*N);
|
||||
|
||||
mp_float_t elem = 1.0;
|
||||
// initialise the unit matrix
|
||||
memset(unit, 0, sizeof(mp_float_t)*N*N);
|
||||
for(size_t m=0; m < N; m++) {
|
||||
memcpy(&unit[m*(N+1)], &elem, sizeof(mp_float_t));
|
||||
}
|
||||
for(size_t m=0; m < N; m++){
|
||||
// this could be faster with ((c < epsilon) && (c > -epsilon))
|
||||
if(abs(data[m*(N+1)]) < epsilon) {
|
||||
m_del(mp_float_t, unit, N*N);
|
||||
return false;
|
||||
}
|
||||
for(size_t n=0; n < N; n++){
|
||||
if(m != n){
|
||||
elem = data[N*n+m] / data[m*(N+1)];
|
||||
for(size_t k=0; k < N; k++){
|
||||
data[N*n+k] -= elem * data[N*m+k];
|
||||
unit[N*n+k] -= elem * unit[N*m+k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(size_t m=0; m < N; m++){
|
||||
elem = data[m*(N+1)];
|
||||
for(size_t n=0; n < N; n++){
|
||||
data[N*m+n] /= elem;
|
||||
unit[N*m+n] /= elem;
|
||||
}
|
||||
}
|
||||
memcpy(data, unit, sizeof(mp_float_t)*N*N);
|
||||
m_del(mp_float_t, unit, N*N);
|
||||
return true;
|
||||
}
|
||||
|
||||
mp_obj_t linalg_inv(mp_obj_t o_in) {
|
||||
// since inv is not a class method, we have to inspect the input argument first
|
||||
if(!MP_OBJ_IS_TYPE(o_in, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError("only ndarrays can be inverted");
|
||||
}
|
||||
ndarray_obj_t *o = MP_OBJ_TO_PTR(o_in);
|
||||
if(!MP_OBJ_IS_TYPE(o_in, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError("only ndarray objects can be inverted");
|
||||
}
|
||||
if(o->m != o->n) {
|
||||
mp_raise_ValueError("only square matrices can be inverted");
|
||||
}
|
||||
ndarray_obj_t *inverted = create_new_ndarray(o->m, o->n, NDARRAY_FLOAT);
|
||||
mp_float_t *data = (mp_float_t *)inverted->array->items;
|
||||
mp_obj_t elem;
|
||||
for(size_t m=0; m < o->m; m++) { // rows first
|
||||
for(size_t n=0; n < o->n; n++) { // columns next
|
||||
// this could, perhaps, be done in single line...
|
||||
// On the other hand, we probably spend little time here
|
||||
elem = mp_binary_get_val_array(o->array->typecode, o->array->items, m*o->n+n);
|
||||
data[m*o->n+n] = (mp_float_t)mp_obj_get_float(elem);
|
||||
}
|
||||
}
|
||||
|
||||
if(!linalg_invert_matrix(data, o->m)) {
|
||||
// TODO: I am not sure this is needed here. Otherwise,
|
||||
// how should we free up the unused RAM of inverted?
|
||||
m_del(mp_float_t, inverted->array->items, o->n*o->n);
|
||||
mp_raise_ValueError("input matrix is singular");
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(inverted);
|
||||
}
|
||||
|
||||
mp_obj_t linalg_dot(mp_obj_t _m1, mp_obj_t _m2) {
|
||||
// TODO: should the results be upcast?
|
||||
ndarray_obj_t *m1 = MP_OBJ_TO_PTR(_m1);
|
||||
ndarray_obj_t *m2 = MP_OBJ_TO_PTR(_m2);
|
||||
if(m1->n != m2->m) {
|
||||
mp_raise_ValueError("matrix dimensions do not match");
|
||||
}
|
||||
// TODO: numpy uses upcasting here
|
||||
ndarray_obj_t *out = create_new_ndarray(m1->m, m2->n, NDARRAY_FLOAT);
|
||||
mp_float_t *outdata = (mp_float_t *)out->array->items;
|
||||
mp_float_t sum, v1, v2;
|
||||
for(size_t i=0; i < m1->n; i++) {
|
||||
for(size_t j=0; j < m2->m; j++) {
|
||||
sum = 0.0;
|
||||
for(size_t k=0; k < m1->m; k++) {
|
||||
// (i, k) * (k, j)
|
||||
v1 = ndarray_get_float_value(m1->array->items, m1->array->typecode, i*m1->n+k);
|
||||
v2 = ndarray_get_float_value(m2->array->items, m2->array->typecode, k*m2->n+j);
|
||||
sum += v1 * v2;
|
||||
}
|
||||
outdata[i*m1->m+j] = sum;
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(out);
|
||||
}
|
||||
|
||||
mp_obj_t linalg_zeros_ones(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, uint8_t kind) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} } ,
|
||||
{ MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = NDARRAY_FLOAT} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
uint8_t dtype = args[1].u_int;
|
||||
if(!mp_obj_is_int(args[0].u_obj) && !mp_obj_is_type(args[0].u_obj, &mp_type_tuple)) {
|
||||
mp_raise_TypeError("input argument must be an integer or a 2-tuple");
|
||||
}
|
||||
ndarray_obj_t *ndarray = NULL;
|
||||
if(mp_obj_is_int(args[0].u_obj)) {
|
||||
size_t n = mp_obj_get_int(args[0].u_obj);
|
||||
ndarray = create_new_ndarray(1, n, dtype);
|
||||
} else if(mp_obj_is_type(args[0].u_obj, &mp_type_tuple)) {
|
||||
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
if(tuple->len != 2) {
|
||||
mp_raise_TypeError("input argument must be an integer or a 2-tuple");
|
||||
}
|
||||
ndarray = create_new_ndarray(mp_obj_get_int(tuple->items[0]),
|
||||
mp_obj_get_int(tuple->items[1]), dtype);
|
||||
}
|
||||
if(kind == 1) {
|
||||
mp_obj_t one = mp_obj_new_int(1);
|
||||
for(size_t i=0; i < ndarray->array->len; i++) {
|
||||
mp_binary_set_val_array(dtype, ndarray->array->items, i, one);
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
|
||||
mp_obj_t linalg_zeros(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return linalg_zeros_ones(n_args, pos_args, kw_args, 0);
|
||||
}
|
||||
|
||||
mp_obj_t linalg_ones(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return linalg_zeros_ones(n_args, pos_args, kw_args, 1);
|
||||
}
|
||||
|
||||
mp_obj_t linalg_eye(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
|
||||
{ MP_QSTR_M, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj) } },
|
||||
{ MP_QSTR_k, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||
{ MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = NDARRAY_FLOAT} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
size_t n = args[0].u_int, m;
|
||||
int16_t k = args[2].u_int;
|
||||
uint8_t dtype = args[3].u_int;
|
||||
if(args[1].u_rom_obj == mp_const_none) {
|
||||
m = n;
|
||||
} else {
|
||||
m = mp_obj_get_int(args[1].u_rom_obj);
|
||||
}
|
||||
|
||||
ndarray_obj_t *ndarray = create_new_ndarray(m, n, dtype);
|
||||
mp_obj_t one = mp_obj_new_int(1);
|
||||
size_t i = 0;
|
||||
if((k >= 0) && (k < n)) {
|
||||
while(k < n) {
|
||||
mp_binary_set_val_array(dtype, ndarray->array->items, i*n+k, one);
|
||||
k++;
|
||||
i++;
|
||||
}
|
||||
} else if((k < 0) && (-k < m)) {
|
||||
k = -k;
|
||||
i = 0;
|
||||
while(k < m) {
|
||||
mp_binary_set_val_array(dtype, ndarray->array->items, k*n+i, one);
|
||||
k++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
|
||||
mp_obj_t linalg_det(mp_obj_t oin) {
|
||||
if(!mp_obj_is_type(oin, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError("function defined for ndarrays only");
|
||||
}
|
||||
ndarray_obj_t *in = MP_OBJ_TO_PTR(oin);
|
||||
if(in->m != in->n) {
|
||||
mp_raise_ValueError("input must be square matrix");
|
||||
}
|
||||
|
||||
mp_float_t *tmp = m_new(mp_float_t, in->n*in->n);
|
||||
for(size_t i=0; i < in->array->len; i++){
|
||||
tmp[i] = ndarray_get_float_value(in->array->items, in->array->typecode, i);
|
||||
}
|
||||
mp_float_t c;
|
||||
for(size_t m=0; m < in->m-1; m++){
|
||||
if(abs(tmp[m*(in->n+1)]) < epsilon) {
|
||||
m_del(mp_float_t, tmp, in->n*in->n);
|
||||
return mp_obj_new_float(0.0);
|
||||
}
|
||||
for(size_t n=0; n < in->n; n++){
|
||||
if(m != n) {
|
||||
c = tmp[in->n*n+m] / tmp[m*(in->n+1)];
|
||||
for(size_t k=0; k < in->n; k++){
|
||||
tmp[in->n*n+k] -= c * tmp[in->n*m+k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mp_float_t det = 1.0;
|
||||
|
||||
for(size_t m=0; m < in->m; m++){
|
||||
det *= tmp[m*(in->n+1)];
|
||||
}
|
||||
m_del(mp_float_t, tmp, in->n*in->n);
|
||||
return mp_obj_new_float(det);
|
||||
}
|
||||
|
||||
mp_obj_t linalg_eig(mp_obj_t oin) {
|
||||
if(!mp_obj_is_type(oin, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError("function defined for ndarrays only");
|
||||
}
|
||||
ndarray_obj_t *in = MP_OBJ_TO_PTR(oin);
|
||||
if(in->m != in->n) {
|
||||
mp_raise_ValueError("input must be square matrix");
|
||||
}
|
||||
mp_float_t *array = m_new(mp_float_t, in->array->len);
|
||||
for(size_t i=0; i < in->array->len; i++) {
|
||||
array[i] = ndarray_get_float_value(in->array->items, in->array->typecode, i);
|
||||
}
|
||||
// make sure the matrix is symmetric
|
||||
for(size_t m=0; m < in->m; m++) {
|
||||
for(size_t n=m+1; n < in->n; n++) {
|
||||
// compare entry (m, n) to (n, m)
|
||||
// TODO: this must probably be scaled!
|
||||
if(epsilon < abs(array[m*in->n + n] - array[n*in->n + m])) {
|
||||
mp_raise_ValueError("input matrix is asymmetric");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we got this far, then the matrix will be symmetric
|
||||
|
||||
ndarray_obj_t *eigenvectors = create_new_ndarray(in->m, in->n, NDARRAY_FLOAT);
|
||||
mp_float_t *eigvectors = (mp_float_t *)eigenvectors->array->items;
|
||||
// start out with the unit matrix
|
||||
for(size_t m=0; m < in->m; m++) {
|
||||
eigvectors[m*(in->n+1)] = 1.0;
|
||||
}
|
||||
mp_float_t largest, w, t, c, s, tau, aMk, aNk, vm, vn;
|
||||
size_t M, N;
|
||||
size_t iterations = JACOBI_MAX*in->n*in->n;
|
||||
do {
|
||||
iterations--;
|
||||
// find the pivot here
|
||||
M = 0;
|
||||
N = 0;
|
||||
largest = 0.0;
|
||||
for(size_t m=0; m < in->m-1; m++) { // -1: no need to inspect last row
|
||||
for(size_t n=m+1; n < in->n; n++) {
|
||||
w = fabs(array[m*in->n + n]);
|
||||
if((largest < w) && (epsilon < w)) {
|
||||
M = m;
|
||||
N = n;
|
||||
largest = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(M+N == 0) { // all entries are smaller than epsilon, there is not much we can do...
|
||||
break;
|
||||
}
|
||||
// at this point, we have the pivot, and it is the entry (M, N)
|
||||
// now we have to find the rotation angle
|
||||
w = (array[N*in->n + N] - array[M*in->n + M]) / (2.0*array[M*in->n + N]);
|
||||
// The following if/else chooses the smaller absolute value for the tangent
|
||||
// of the rotation angle. Going with the smaller should be numerically stabler.
|
||||
if(w > 0) {
|
||||
t = MICROPY_FLOAT_C_FUN(sqrt)(w*w + 1.0) - w;
|
||||
} else {
|
||||
t = -1.0*(MICROPY_FLOAT_C_FUN(sqrt)(w*w + 1.0) + w);
|
||||
}
|
||||
s = t / MICROPY_FLOAT_C_FUN(sqrt)(t*t + 1.0); // the sine of the rotation angle
|
||||
c = 1.0 / MICROPY_FLOAT_C_FUN(sqrt)(t*t + 1.0); // the cosine of the rotation angle
|
||||
tau = (1.0-c)/s; // this is equal to the tangent of the half of the rotation angle
|
||||
|
||||
// at this point, we have the rotation angles, so we can transform the matrix
|
||||
// first the two diagonal elements
|
||||
// a(M, M) = a(M, M) - t*a(M, N)
|
||||
array[M*in->n + M] = array[M*in->n + M] - t * array[M*in->n + N];
|
||||
// a(N, N) = a(N, N) + t*a(M, N)
|
||||
array[N*in->n + N] = array[N*in->n + N] + t * array[M*in->n + N];
|
||||
// after the rotation, the a(M, N), and a(N, M) entries should become zero
|
||||
array[M*in->n + N] = array[N*in->n + M] = 0.0;
|
||||
// then all other elements in the column
|
||||
for(size_t k=0; k < in->m; k++) {
|
||||
if((k == M) || (k == N)) {
|
||||
continue;
|
||||
}
|
||||
aMk = array[M*in->n + k];
|
||||
aNk = array[N*in->n + k];
|
||||
// a(M, k) = a(M, k) - s*(a(N, k) + tau*a(M, k))
|
||||
array[M*in->n + k] -= s*(aNk + tau*aMk);
|
||||
// a(N, k) = a(N, k) + s*(a(M, k) - tau*a(N, k))
|
||||
array[N*in->n + k] += s*(aMk - tau*aNk);
|
||||
// a(k, M) = a(M, k)
|
||||
array[k*in->n + M] = array[M*in->n + k];
|
||||
// a(k, N) = a(N, k)
|
||||
array[k*in->n + N] = array[N*in->n + k];
|
||||
}
|
||||
// now we have to update the eigenvectors
|
||||
// the rotation matrix, R, multiplies from the right
|
||||
// R is the unit matrix, except for the
|
||||
// R(M,M) = R(N, N) = c
|
||||
// R(N, M) = s
|
||||
// (M, N) = -s
|
||||
// entries. This means that only the Mth, and Nth columns will change
|
||||
for(size_t m=0; m < in->m; m++) {
|
||||
vm = eigvectors[m*in->n+M];
|
||||
vn = eigvectors[m*in->n+N];
|
||||
// the new value of eigvectors(m, M)
|
||||
eigvectors[m*in->n+M] = c * vm - s * vn;
|
||||
// the new value of eigvectors(m, N)
|
||||
eigvectors[m*in->n+N] = s * vm + c * vn;
|
||||
}
|
||||
} while(iterations > 0);
|
||||
|
||||
if(iterations == 0) {
|
||||
// the computation did not converge; numpy raises LinAlgError
|
||||
m_del(mp_float_t, array, in->array->len);
|
||||
mp_raise_ValueError("iterations did not converge");
|
||||
}
|
||||
ndarray_obj_t *eigenvalues = create_new_ndarray(1, in->n, NDARRAY_FLOAT);
|
||||
mp_float_t *eigvalues = (mp_float_t *)eigenvalues->array->items;
|
||||
for(size_t i=0; i < in->n; i++) {
|
||||
eigvalues[i] = array[i*(in->n+1)];
|
||||
}
|
||||
m_del(mp_float_t, array, in->array->len);
|
||||
|
||||
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
|
||||
tuple->items[0] = MP_OBJ_FROM_PTR(eigenvalues);
|
||||
tuple->items[1] = MP_OBJ_FROM_PTR(eigenvectors);
|
||||
return tuple;
|
||||
return MP_OBJ_FROM_PTR(eigenvalues);
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _LINALG_
|
||||
#define _LINALG_
|
||||
|
||||
#include "ndarray.h"
|
||||
|
||||
#define SWAP(t, a, b) { t tmp = a; a = b; b = tmp; }
|
||||
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
#define epsilon 1.2e-7
|
||||
#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
|
||||
#define epsilon 2.3e-16
|
||||
#endif
|
||||
|
||||
#define JACOBI_MAX 20
|
||||
|
||||
mp_obj_t linalg_transpose(mp_obj_t );
|
||||
mp_obj_t linalg_reshape(mp_obj_t , mp_obj_t );
|
||||
mp_obj_t linalg_size(size_t , const mp_obj_t *, mp_map_t *);
|
||||
bool linalg_invert_matrix(mp_float_t *, size_t );
|
||||
mp_obj_t linalg_inv(mp_obj_t );
|
||||
mp_obj_t linalg_dot(mp_obj_t , mp_obj_t );
|
||||
mp_obj_t linalg_zeros(size_t , const mp_obj_t *, mp_map_t *);
|
||||
mp_obj_t linalg_ones(size_t , const mp_obj_t *, mp_map_t *);
|
||||
mp_obj_t linalg_eye(size_t , const mp_obj_t *, mp_map_t *);
|
||||
|
||||
mp_obj_t linalg_det(mp_obj_t );
|
||||
mp_obj_t linalg_eig(mp_obj_t );
|
||||
|
||||
#endif
|
||||
18
code/micropython.cmake
Normal file
18
code/micropython.cmake
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
add_library(usermod_ulab INTERFACE)
|
||||
|
||||
file(GLOB_RECURSE ULAB_SOURCES ${CMAKE_CURRENT_LIST_DIR}/*.c)
|
||||
|
||||
target_sources(usermod_ulab INTERFACE
|
||||
${ULAB_SOURCES}
|
||||
)
|
||||
|
||||
target_include_directories(usermod_ulab INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
)
|
||||
|
||||
target_compile_definitions(usermod_ulab INTERFACE
|
||||
MODULE_ULAB_ENABLED=1
|
||||
)
|
||||
|
||||
target_link_libraries(usermod INTERFACE usermod_ulab)
|
||||
|
||||
|
|
@ -2,14 +2,41 @@
|
|||
USERMODULES_DIR := $(USERMOD_DIR)
|
||||
|
||||
# Add all C files to SRC_USERMOD.
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/scipy/integrate/integrate.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/scipy/linalg/linalg.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/scipy/optimize/optimize.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/scipy/signal/signal.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/scipy/special/special.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/ndarray_operators.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/ulab_tools.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/ndarray.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/linalg.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/vectorise.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/poly.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/fft.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numerical.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numpy/ndarray/ndarray_iter.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/ndarray_properties.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numpy/approx.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numpy/bitwise.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numpy/compare.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numpy/carray/carray.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numpy/carray/carray_tools.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numpy/create.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numpy/fft/fft.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numpy/fft/fft_tools.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numpy/filter.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numpy/io/io.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numpy/linalg/linalg.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numpy/linalg/linalg_tools.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numpy/numerical.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numpy/poly.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numpy/random/random.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numpy/stats.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numpy/transform.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numpy/vector.c
|
||||
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/numpy/numpy.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/scipy/scipy.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/user/user.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/utils/utils.c
|
||||
SRC_USERMOD += $(USERMODULES_DIR)/ulab.c
|
||||
|
||||
# We can add our module folder to include paths if needed
|
||||
# This is not actually needed in this example.
|
||||
CFLAGS_USERMOD += -I$(USERMODULES_DIR)
|
||||
|
||||
override CFLAGS_EXTRA += -DMODULE_ULAB_ENABLED=1
|
||||
|
|
|
|||
2524
code/ndarray.c
2524
code/ndarray.c
File diff suppressed because it is too large
Load diff
811
code/ndarray.h
811
code/ndarray.h
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
|
|
@ -5,7 +6,8 @@
|
|||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Zoltán Vörös
|
||||
* Copyright (c) 2019-2021 Zoltán Vörös
|
||||
* 2020 Jeff Epler for Adafruit Industries
|
||||
*/
|
||||
|
||||
#ifndef _NDARRAY_
|
||||
|
|
@ -16,7 +18,14 @@
|
|||
#include "py/objstr.h"
|
||||
#include "py/objlist.h"
|
||||
|
||||
#define PRINT_MAX 10
|
||||
#include "ulab.h"
|
||||
|
||||
#ifndef MP_PI
|
||||
#define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846)
|
||||
#endif
|
||||
#ifndef MP_E
|
||||
#define MP_E MICROPY_FLOAT_CONST(2.71828182845904523536)
|
||||
#endif
|
||||
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
#define FLOAT_TYPECODE 'f'
|
||||
|
|
@ -24,101 +33,769 @@
|
|||
#define FLOAT_TYPECODE 'd'
|
||||
#endif
|
||||
|
||||
const mp_obj_type_t ulab_ndarray_type;
|
||||
#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B
|
||||
|
||||
// For object representations A and B a Python float object is allocated as a
|
||||
// concrete object in a struct, with the first entry pointing to &mp_type_float.
|
||||
// Constant float objects are a struct in ROM and are referenced via their pointer.
|
||||
|
||||
// Use ULAB_DEFINE_FLOAT_CONST to define a constant float object.
|
||||
// id is the name of the constant, num is its floating point value.
|
||||
// hex32 is computed as: hex(int.from_bytes(array.array('f', [num]), 'little'))
|
||||
// hex64 is computed as: hex(int.from_bytes(array.array('d', [num]), 'little'))
|
||||
|
||||
// Use ULAB_REFERENCE_FLOAT_CONST to reference a constant float object in code.
|
||||
|
||||
#define ULAB_DEFINE_FLOAT_CONST(id, num, hex32, hex64) \
|
||||
const mp_obj_float_t id##_obj = {{&mp_type_float}, (num)}
|
||||
|
||||
#define ULAB_REFERENCE_FLOAT_CONST(id) MP_ROM_PTR(&id##_obj)
|
||||
|
||||
// this typedef is lifted from objfloat.c, because mp_obj_float_t is not exposed
|
||||
typedef struct _mp_obj_float_t {
|
||||
mp_obj_base_t base;
|
||||
mp_float_t value;
|
||||
} mp_obj_float_t;
|
||||
|
||||
#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C
|
||||
|
||||
// For object representation C a Python float object is stored directly in the
|
||||
// mp_obj_t value.
|
||||
|
||||
// See above for how to use ULAB_DEFINE_FLOAT_CONST and ULAB_REFERENCE_FLOAT_CONST.
|
||||
|
||||
#define ULAB_DEFINE_FLOAT_CONST(id, num, hex32, hex64) \
|
||||
enum { \
|
||||
id = (((((uint32_t)hex32) & ~3) | 2) + 0x80800000) \
|
||||
}
|
||||
|
||||
#define ULAB_REFERENCE_FLOAT_CONST(id) ((mp_obj_t)(id))
|
||||
|
||||
#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D
|
||||
|
||||
// For object representation D (nan-boxing) a Python float object is stored
|
||||
// directly in the mp_obj_t value.
|
||||
|
||||
// See above for how to use ULAB_DEFINE_FLOAT_CONST and ULAB_REFERENCE_FLOAT_CONST.
|
||||
|
||||
#define ULAB_DEFINE_FLOAT_CONST(id, num, hex32, hex64) \
|
||||
const uint64_t id = (((uint64_t)hex64) + 0x8004000000000000ULL)
|
||||
|
||||
#define ULAB_REFERENCE_FLOAT_CONST(id) {id}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(MICROPY_VERSION_MAJOR) && MICROPY_VERSION_MAJOR == 1 && MICROPY_VERSION_MINOR == 11
|
||||
typedef struct _mp_obj_slice_t {
|
||||
mp_obj_base_t base;
|
||||
mp_obj_t start;
|
||||
mp_obj_t stop;
|
||||
mp_obj_t step;
|
||||
} mp_obj_slice_t;
|
||||
#define MP_ERROR_TEXT(x) x
|
||||
#endif
|
||||
|
||||
#if !defined(MP_OBJ_TYPE_GET_SLOT)
|
||||
#if defined(MP_TYPE_FLAG_EXTENDED)
|
||||
// Provide MP_OBJ_TYPE_{HAS,GET}_SLOT for CircuitPython.
|
||||
#define MP_OBJ_TYPE_HAS_SLOT(t, f) (mp_type_get_##f##_slot(t) != NULL)
|
||||
#define MP_OBJ_TYPE_GET_SLOT(t, f) mp_type_get_##f##_slot(t)
|
||||
#else
|
||||
// Provide MP_OBJ_TYPE_{HAS,GET}_SLOT for older revisions of MicroPython.
|
||||
#define MP_OBJ_TYPE_HAS_SLOT(t, f) ((t)->f != NULL)
|
||||
#define MP_OBJ_TYPE_GET_SLOT(t, f) (t)->f
|
||||
|
||||
// Also allow CiruitPython-style mp_obj_type_t definitions.
|
||||
#define MP_TYPE_FLAG_EXTENDED (0)
|
||||
#define MP_TYPE_EXTENDED_FIELDS(...) __VA_ARGS__
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define ndarray_set_value(a, b, c, d) mp_binary_set_val_array(a, b, c, d)
|
||||
void ndarray_set_complex_value(void *, size_t , mp_obj_t );
|
||||
|
||||
#define NDARRAY_NUMERIC 0
|
||||
#define NDARRAY_BOOLEAN 1
|
||||
|
||||
#define NDARRAY_NDARRAY_TYPE 1
|
||||
#define NDARRAY_ITERABLE_TYPE 2
|
||||
|
||||
extern const mp_obj_type_t ulab_ndarray_type;
|
||||
|
||||
enum NDARRAY_TYPE {
|
||||
NDARRAY_BOOL = '?', // this must never be assigned to the dtype!
|
||||
NDARRAY_UINT8 = 'B',
|
||||
NDARRAY_INT8 = 'b',
|
||||
NDARRAY_UINT16 = 'H',
|
||||
NDARRAY_INT16 = 'h',
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
NDARRAY_COMPLEX = 'c',
|
||||
#endif
|
||||
NDARRAY_FLOAT = FLOAT_TYPECODE,
|
||||
};
|
||||
|
||||
typedef struct _ndarray_obj_t {
|
||||
mp_obj_base_t base;
|
||||
size_t m, n;
|
||||
uint8_t dtype;
|
||||
uint8_t itemsize;
|
||||
uint8_t boolean;
|
||||
uint8_t ndim;
|
||||
size_t len;
|
||||
mp_obj_array_t *array;
|
||||
size_t bytes;
|
||||
size_t shape[ULAB_MAX_DIMS];
|
||||
int32_t strides[ULAB_MAX_DIMS];
|
||||
void *array;
|
||||
void *origin;
|
||||
} ndarray_obj_t;
|
||||
|
||||
mp_obj_t mp_obj_new_ndarray_iterator(mp_obj_t , size_t , mp_obj_iter_buf_t *);
|
||||
#if ULAB_HAS_DTYPE_OBJECT
|
||||
extern const mp_obj_type_t ulab_dtype_type;
|
||||
|
||||
mp_float_t ndarray_get_float_value(void *, uint8_t , size_t );
|
||||
typedef struct _dtype_obj_t {
|
||||
mp_obj_base_t base;
|
||||
uint8_t dtype;
|
||||
} dtype_obj_t;
|
||||
|
||||
void ndarray_dtype_print(const mp_print_t *, mp_obj_t , mp_print_kind_t );
|
||||
|
||||
mp_obj_t ndarray_dtype_make_new(const mp_obj_type_t *, size_t , size_t , const mp_obj_t *);
|
||||
#endif /* ULAB_HAS_DTYPE_OBJECT */
|
||||
|
||||
extern const mp_obj_type_t ndarray_flatiter_type;
|
||||
|
||||
mp_obj_t ndarray_new_ndarray_iterator(mp_obj_t , mp_obj_iter_buf_t *);
|
||||
|
||||
mp_obj_t ndarray_get_item(ndarray_obj_t *, void *);
|
||||
mp_float_t ndarray_get_float_value(void *, uint8_t );
|
||||
mp_float_t ndarray_get_float_index(void *, uint8_t , size_t );
|
||||
bool ndarray_object_is_array_like(mp_obj_t );
|
||||
void fill_array_iterable(mp_float_t *, mp_obj_t );
|
||||
size_t *ndarray_shape_vector(size_t , size_t , size_t , size_t );
|
||||
|
||||
void ndarray_print_row(const mp_print_t *, mp_obj_array_t *, size_t , size_t );
|
||||
void ndarray_print(const mp_print_t *, mp_obj_t , mp_print_kind_t );
|
||||
void ndarray_assign_elements(mp_obj_array_t *, mp_obj_t , uint8_t , size_t *);
|
||||
ndarray_obj_t *create_new_ndarray(size_t , size_t , uint8_t );
|
||||
|
||||
mp_obj_t ndarray_copy(mp_obj_t );
|
||||
#if ULAB_HAS_PRINTOPTIONS
|
||||
mp_obj_t ndarray_set_printoptions(size_t , const mp_obj_t *, mp_map_t *);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(ndarray_set_printoptions_obj);
|
||||
|
||||
mp_obj_t ndarray_get_printoptions(void);
|
||||
MP_DECLARE_CONST_FUN_OBJ_0(ndarray_get_printoptions_obj);
|
||||
#endif
|
||||
|
||||
void ndarray_assign_elements(ndarray_obj_t *, mp_obj_t , uint8_t , size_t *);
|
||||
size_t *ndarray_contract_shape(ndarray_obj_t *, uint8_t );
|
||||
int32_t *ndarray_contract_strides(ndarray_obj_t *, uint8_t );
|
||||
|
||||
ndarray_obj_t *ndarray_from_iterable(mp_obj_t , uint8_t );
|
||||
ndarray_obj_t *ndarray_new_dense_ndarray(uint8_t , size_t *, uint8_t );
|
||||
ndarray_obj_t *ndarray_new_ndarray_from_tuple(mp_obj_tuple_t *, uint8_t );
|
||||
ndarray_obj_t *ndarray_new_ndarray(uint8_t , size_t *, int32_t *, uint8_t , uint8_t *);
|
||||
ndarray_obj_t *ndarray_new_linear_array(size_t , uint8_t );
|
||||
ndarray_obj_t *ndarray_new_view(ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t );
|
||||
bool ndarray_is_dense(ndarray_obj_t *);
|
||||
ndarray_obj_t *ndarray_copy_view(ndarray_obj_t *);
|
||||
ndarray_obj_t *ndarray_copy_view_convert_type(ndarray_obj_t *, uint8_t );
|
||||
void ndarray_copy_array(ndarray_obj_t *, ndarray_obj_t *, uint8_t );
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(ndarray_array_constructor_obj);
|
||||
mp_obj_t ndarray_make_new(const mp_obj_type_t *, size_t , size_t , const mp_obj_t *);
|
||||
mp_obj_t ndarray_subscr(mp_obj_t , mp_obj_t , mp_obj_t );
|
||||
mp_obj_t ndarray_getiter(mp_obj_t , mp_obj_iter_buf_t *);
|
||||
bool ndarray_can_broadcast(ndarray_obj_t *, ndarray_obj_t *, uint8_t *, size_t *, int32_t *, int32_t *);
|
||||
bool ndarray_can_broadcast_inplace(ndarray_obj_t *, ndarray_obj_t *, int32_t *);
|
||||
mp_obj_t ndarray_binary_op(mp_binary_op_t , mp_obj_t , mp_obj_t );
|
||||
mp_obj_t ndarray_unary_op(mp_unary_op_t , mp_obj_t );
|
||||
|
||||
mp_obj_t ndarray_shape(mp_obj_t );
|
||||
mp_obj_t ndarray_rawsize(mp_obj_t );
|
||||
size_t *ndarray_new_coords(uint8_t );
|
||||
void ndarray_rewind_array(uint8_t , uint8_t *, size_t *, int32_t *, size_t *);
|
||||
|
||||
// various ndarray methods
|
||||
#if NDARRAY_HAS_BYTESWAP
|
||||
mp_obj_t ndarray_byteswap(size_t , const mp_obj_t *, mp_map_t *);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(ndarray_byteswap_obj);
|
||||
#endif
|
||||
|
||||
#if NDARRAY_HAS_COPY
|
||||
mp_obj_t ndarray_copy(mp_obj_t );
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(ndarray_copy_obj);
|
||||
#endif
|
||||
|
||||
#if NDARRAY_HAS_FLATTEN
|
||||
mp_obj_t ndarray_flatten(size_t , const mp_obj_t *, mp_map_t *);
|
||||
mp_obj_t ndarray_asbytearray(mp_obj_t );
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(ndarray_flatten_obj);
|
||||
#endif
|
||||
|
||||
#define CREATE_SINGLE_ITEM(outarray, type, typecode, value) do {\
|
||||
ndarray_obj_t *tmp = create_new_ndarray(1, 1, (typecode));\
|
||||
type *tmparr = (type *)tmp->array->items;\
|
||||
tmparr[0] = (type)(value);\
|
||||
(outarray) = MP_OBJ_FROM_PTR(tmp);\
|
||||
} while(0)
|
||||
#if NDARRAY_HAS_DTYPE
|
||||
mp_obj_t ndarray_dtype(mp_obj_t );
|
||||
#endif
|
||||
|
||||
/*
|
||||
mp_obj_t row = mp_obj_new_list(n, NULL);
|
||||
mp_obj_list_t *row_ptr = MP_OBJ_TO_PTR(row);
|
||||
#if NDARRAY_HAS_ITEMSIZE
|
||||
mp_obj_t ndarray_itemsize(mp_obj_t );
|
||||
#endif
|
||||
|
||||
should work outside the loop, but it doesn't. Go figure!
|
||||
*/
|
||||
#if NDARRAY_HAS_NDIM
|
||||
mp_obj_t ndarray_ndim(mp_obj_t );
|
||||
#endif
|
||||
|
||||
#define RUN_BINARY_LOOP(typecode, type_out, type_left, type_right, ol, or, op) do {\
|
||||
type_left *left = (type_left *)(ol)->array->items;\
|
||||
type_right *right = (type_right *)(or)->array->items;\
|
||||
uint8_t inc = 0;\
|
||||
if((or)->array->len > 1) inc = 1;\
|
||||
if(((op) == MP_BINARY_OP_ADD) || ((op) == MP_BINARY_OP_SUBTRACT) || ((op) == MP_BINARY_OP_MULTIPLY)) {\
|
||||
ndarray_obj_t *out = create_new_ndarray(ol->m, ol->n, typecode);\
|
||||
type_out *(odata) = (type_out *)out->array->items;\
|
||||
if((op) == MP_BINARY_OP_ADD) { for(size_t i=0, j=0; i < (ol)->array->len; i++, j+=inc) odata[i] = left[i] + right[j];}\
|
||||
if((op) == MP_BINARY_OP_SUBTRACT) { for(size_t i=0, j=0; i < (ol)->array->len; i++, j+=inc) odata[i] = left[i] - right[j];}\
|
||||
if((op) == MP_BINARY_OP_MULTIPLY) { for(size_t i=0, j=0; i < (ol)->array->len; i++, j+=inc) odata[i] = left[i] * right[j];}\
|
||||
return MP_OBJ_FROM_PTR(out);\
|
||||
} else if((op) == MP_BINARY_OP_TRUE_DIVIDE) {\
|
||||
ndarray_obj_t *out = create_new_ndarray(ol->m, ol->n, NDARRAY_FLOAT);\
|
||||
mp_float_t *odata = (mp_float_t *)out->array->items;\
|
||||
for(size_t i=0, j=0; i < (ol)->array->len; i++, j+=inc) odata[i] = (mp_float_t)left[i]/(mp_float_t)right[j];\
|
||||
return MP_OBJ_FROM_PTR(out);\
|
||||
} else if(((op) == MP_BINARY_OP_LESS) || ((op) == MP_BINARY_OP_LESS_EQUAL) || \
|
||||
((op) == MP_BINARY_OP_MORE) || ((op) == MP_BINARY_OP_MORE_EQUAL)) {\
|
||||
mp_obj_t out_list = mp_obj_new_list(0, NULL);\
|
||||
size_t m = (ol)->m, n = (ol)->n;\
|
||||
for(size_t i=0, r=0; i < m; i++, r+=inc) {\
|
||||
mp_obj_t row = mp_obj_new_list(n, NULL);\
|
||||
mp_obj_list_t *row_ptr = MP_OBJ_TO_PTR(row);\
|
||||
for(size_t j=0, s=0; j < n; j++, s+=inc) {\
|
||||
row_ptr->items[j] = mp_const_false;\
|
||||
if((op) == MP_BINARY_OP_LESS) {\
|
||||
if(left[i*n+j] < right[r*n+s]) row_ptr->items[j] = mp_const_true;\
|
||||
} else if((op) == MP_BINARY_OP_LESS_EQUAL) {\
|
||||
if(left[i*n+j] <= right[r*n+s]) row_ptr->items[j] = mp_const_true;\
|
||||
} else if((op) == MP_BINARY_OP_MORE) {\
|
||||
if(left[i*n+j] > right[r*n+s]) row_ptr->items[j] = mp_const_true;\
|
||||
} else if((op) == MP_BINARY_OP_MORE_EQUAL) {\
|
||||
if(left[i*n+j] >= right[r*n+s]) row_ptr->items[j] = mp_const_true;\
|
||||
#if NDARRAY_HAS_SIZE
|
||||
mp_obj_t ndarray_size(mp_obj_t );
|
||||
#endif
|
||||
|
||||
#if NDARRAY_HAS_SHAPE
|
||||
mp_obj_t ndarray_shape(mp_obj_t );
|
||||
#endif
|
||||
|
||||
#if NDARRAY_HAS_STRIDES
|
||||
mp_obj_t ndarray_strides(mp_obj_t );
|
||||
#endif
|
||||
|
||||
#if NDARRAY_HAS_RESHAPE
|
||||
mp_obj_t ndarray_reshape_core(mp_obj_t , mp_obj_t , bool );
|
||||
mp_obj_t ndarray_reshape(mp_obj_t , mp_obj_t );
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(ndarray_reshape_obj);
|
||||
#endif
|
||||
|
||||
#if NDARRAY_HAS_TOBYTES
|
||||
mp_obj_t ndarray_tobytes(mp_obj_t );
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(ndarray_tobytes_obj);
|
||||
#endif
|
||||
|
||||
#if NDARRAY_HAS_TOLIST
|
||||
mp_obj_t ndarray_tolist(mp_obj_t );
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(ndarray_tolist_obj);
|
||||
#endif
|
||||
|
||||
#if NDARRAY_HAS_TRANSPOSE
|
||||
mp_obj_t ndarray_transpose(mp_obj_t );
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(ndarray_transpose_obj);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_NDINFO
|
||||
mp_obj_t ndarray_info(mp_obj_t );
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(ndarray_info_obj);
|
||||
#endif
|
||||
|
||||
mp_int_t ndarray_get_buffer(mp_obj_t , mp_buffer_info_t *, mp_uint_t );
|
||||
//void ndarray_attributes(mp_obj_t , qstr , mp_obj_t *);
|
||||
|
||||
ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t , uint8_t );
|
||||
|
||||
|
||||
#define BOOLEAN_ASSIGNMENT_LOOP(type_left, type_right, ndarray, lstrides, iarray, istride, varray, vstride)\
|
||||
type_left *array = (type_left *)(ndarray)->array;\
|
||||
for(size_t i=0; i < (ndarray)->len; i++) {\
|
||||
if(*(iarray)) {\
|
||||
*array = (type_left)(*((type_right *)(varray)));\
|
||||
(varray) += (vstride);\
|
||||
}\
|
||||
}\
|
||||
if(m == 1) return row;\
|
||||
mp_obj_list_append(out_list, row);\
|
||||
}\
|
||||
return out_list;\
|
||||
}\
|
||||
} while(0)
|
||||
array += (lstrides);\
|
||||
(iarray) += (istride);\
|
||||
} while(0)
|
||||
|
||||
#if ULAB_HAS_FUNCTION_ITERATOR
|
||||
#define BINARY_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
size_t *lcoords = ndarray_new_coords((results)->ndim);\
|
||||
size_t *rcoords = ndarray_new_coords((results)->ndim);\
|
||||
for(size_t i=0; i < (results)->len/(results)->shape[ULAB_MAX_DIMS -1]; i++) {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*array++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
ndarray_rewind_array((results)->ndim, (larray), (results)->shape, (lstrides), lcoords);\
|
||||
ndarray_rewind_array((results)->ndim, (rarray), (results)->shape, (rstrides), rcoords);\
|
||||
} while(0)
|
||||
|
||||
#define INPLACE_LOOP(results, type_left, type_right, larray, rarray, rstrides, OPERATOR)\
|
||||
size_t *lcoords = ndarray_new_coords((results)->ndim);\
|
||||
size_t *rcoords = ndarray_new_coords((results)->ndim);\
|
||||
for(size_t i=0; i < (results)->len/(results)->shape[ULAB_MAX_DIMS -1]; i++) {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
ndarray_rewind_array((results)->ndim, (larray), (results)->shape, (results)->strides, lcoords);\
|
||||
ndarray_rewind_array((results)->ndim, (rarray), (results)->shape, (rstrides), rcoords);\
|
||||
} while(0)
|
||||
|
||||
#define EQUALITY_LOOP(results, array, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
|
||||
size_t *lcoords = ndarray_new_coords((results)->ndim);\
|
||||
size_t *rcoords = ndarray_new_coords((results)->ndim);\
|
||||
for(size_t i=0; i < (results)->len/(results)->shape[ULAB_MAX_DIMS -1]; i++) {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*(array)++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? 1 : 0;\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
ndarray_rewind_array((results)->ndim, (larray), (results)->shape, (lstrides), lcoords);\
|
||||
ndarray_rewind_array((results)->ndim, (rarray), (results)->shape, (rstrides), rcoords);\
|
||||
} while(0)
|
||||
|
||||
#define POWER_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides)\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
size_t *lcoords = ndarray_new_coords((results)->ndim);\
|
||||
size_t *rcoords = ndarray_new_coords((results)->ndim);\
|
||||
for(size_t i=0; i < (results)->len/(results)->shape[ULAB_MAX_DIMS -1]; i++) {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*array++ = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
ndarray_rewind_array((results)->ndim, (larray), (results)->shape, (lstrides), lcoords);\
|
||||
ndarray_rewind_array((results)->ndim, (rarray), (results)->shape, (rstrides), rcoords);\
|
||||
} while(0)
|
||||
|
||||
#else
|
||||
|
||||
#if ULAB_MAX_DIMS == 1
|
||||
#define BINARY_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
|
||||
type_out *array = (type_out *)results->array;\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*array++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
|
||||
#define INPLACE_LOOP(results, type_left, type_right, larray, rarray, rstrides, OPERATOR)\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
|
||||
#define EQUALITY_LOOP(results, array, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*(array)++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? 1 : 0;\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
|
||||
#define POWER_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides)\
|
||||
type_out *array = (type_out *)results->array;\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*array++ = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
|
||||
#endif /* ULAB_MAX_DIMS == 1 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 2
|
||||
#define BINARY_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*array++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
|
||||
#define INPLACE_LOOP(results, type_left, type_right, larray, rarray, rstrides, OPERATOR)\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
|
||||
#define EQUALITY_LOOP(results, array, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*(array)++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? 1 : 0;\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
|
||||
#define POWER_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides)\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*array++ = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
|
||||
#endif /* ULAB_MAX_DIMS == 2 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 3
|
||||
#define BINARY_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
|
||||
type_out *array = (type_out *)results->array;\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*array++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
|
||||
#define INPLACE_LOOP(results, type_left, type_right, larray, rarray, rstrides, OPERATOR)\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
|
||||
#define EQUALITY_LOOP(results, array, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*(array)++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? 1 : 0;\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
|
||||
#define POWER_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides)\
|
||||
type_out *array = (type_out *)results->array;\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*array++ = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
|
||||
#endif /* ULAB_MAX_DIMS == 3 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 4
|
||||
#define BINARY_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
|
||||
type_out *array = (type_out *)results->array;\
|
||||
size_t i = 0;\
|
||||
do {\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*array++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
|
||||
i++;\
|
||||
} while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
|
||||
|
||||
#define INPLACE_LOOP(results, type_left, type_right, larray, rarray, rstrides, OPERATOR)\
|
||||
size_t i = 0;\
|
||||
do {\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 4];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
|
||||
i++;\
|
||||
} while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
|
||||
|
||||
#define EQUALITY_LOOP(results, array, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
|
||||
size_t i = 0;\
|
||||
do {\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*(array)++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? 1 : 0;\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
|
||||
i++;\
|
||||
} while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
|
||||
|
||||
#define POWER_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides)\
|
||||
type_out *array = (type_out *)results->array;\
|
||||
size_t i = 0;\
|
||||
do {\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*array++ = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
|
||||
i++;\
|
||||
} while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
|
||||
|
||||
#endif /* ULAB_MAX_DIMS == 4 */
|
||||
#endif /* ULAB_HAS_FUNCTION_ITERATOR */
|
||||
|
||||
|
||||
// iterator macro for traversing arrays over all dimensions
|
||||
#if ULAB_MAX_DIMS == 1
|
||||
#define ITERATOR_HEAD()\
|
||||
size_t _l_ = 0;\
|
||||
do {
|
||||
|
||||
#define ITERATOR_TAIL(_source_, _source_array_)\
|
||||
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\
|
||||
_l_++;\
|
||||
} while(_l_ < (_source_)->shape[ULAB_MAX_DIMS - 1]);
|
||||
|
||||
#endif /* ULAB_MAX_DIMS == 1 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 2
|
||||
#define ITERATOR_HEAD()\
|
||||
size_t _k_ = 0;\
|
||||
do {\
|
||||
size_t _l_ = 0;\
|
||||
do {
|
||||
|
||||
#define ITERATOR_TAIL(_source_, _source_array_)\
|
||||
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\
|
||||
_l_++;\
|
||||
} while(_l_ < (_source_)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 1] * (_source_)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 2];\
|
||||
_k_++;\
|
||||
} while(_k_ < (_source_)->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif /* ULAB_MAX_DIMS == 2 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 3
|
||||
#define ITERATOR_HEAD()\
|
||||
size_t _j_ = 0;\
|
||||
do {\
|
||||
size_t _k_ = 0;\
|
||||
do {\
|
||||
size_t _l_ = 0;\
|
||||
do {
|
||||
|
||||
#define ITERATOR_TAIL(_source_, _source_array_)\
|
||||
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\
|
||||
_l_++;\
|
||||
} while(_l_ < (_source_)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 1] * (_source_)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 2];\
|
||||
_k_++;\
|
||||
} while(_k_ < (_source_)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 2] * (_source_)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 3];\
|
||||
_j_++;\
|
||||
} while(_j_ < (_source_)->shape[ULAB_MAX_DIMS - 3]);
|
||||
|
||||
#endif /* ULAB_MAX_DIMS == 3 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 4
|
||||
#define ITERATOR_HEAD()\
|
||||
size_t _i_ = 0;\
|
||||
do {\
|
||||
size_t _j_ = 0;\
|
||||
do {\
|
||||
size_t _k_ = 0;\
|
||||
do {\
|
||||
size_t _l_ = 0;\
|
||||
do {
|
||||
|
||||
#define ITERATOR_TAIL(_source_, _source_array_)\
|
||||
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 1];\
|
||||
_l_++;\
|
||||
} while(_l_ < (_source_)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 1] * (_source_)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 2];\
|
||||
_k_++;\
|
||||
} while(_k_ < (_source_)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 2] * (_source_)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 3];\
|
||||
_j_++;\
|
||||
} while(_j_ < (_source_)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
(_source_array_) -= (_source_)->strides[ULAB_MAX_DIMS - 3] * (_source_)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(_source_array_) += (_source_)->strides[ULAB_MAX_DIMS - 4];\
|
||||
_i_++;\
|
||||
} while(_i_ < (_source_)->shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif /* ULAB_MAX_DIMS == 4 */
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
|||
1245
code/ndarray_operators.c
Normal file
1245
code/ndarray_operators.c
Normal file
File diff suppressed because it is too large
Load diff
714
code/ndarray_operators.h
Normal file
714
code/ndarray_operators.h
Normal file
|
|
@ -0,0 +1,714 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2023 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#include "ndarray.h"
|
||||
|
||||
mp_obj_t ndarray_binary_equality(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *, mp_binary_op_t );
|
||||
mp_obj_t ndarray_binary_add(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
|
||||
mp_obj_t ndarray_binary_modulo(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
|
||||
mp_obj_t ndarray_binary_multiply(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
|
||||
mp_obj_t ndarray_binary_more(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *, mp_binary_op_t );
|
||||
mp_obj_t ndarray_binary_power(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
|
||||
mp_obj_t ndarray_binary_subtract(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
|
||||
mp_obj_t ndarray_binary_true_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
|
||||
mp_obj_t ndarray_binary_logical(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *, mp_binary_op_t );
|
||||
mp_obj_t ndarray_binary_floor_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
|
||||
|
||||
mp_obj_t ndarray_inplace_ams(ndarray_obj_t *, ndarray_obj_t *, int32_t *, uint8_t );
|
||||
mp_obj_t ndarray_inplace_modulo(ndarray_obj_t *, ndarray_obj_t *, int32_t *);
|
||||
mp_obj_t ndarray_inplace_power(ndarray_obj_t *, ndarray_obj_t *, int32_t *);
|
||||
mp_obj_t ndarray_inplace_divide(ndarray_obj_t *, ndarray_obj_t *, int32_t *);
|
||||
|
||||
#define UNWRAP_INPLACE_OPERATOR(lhs, larray, rarray, rstrides, OPERATOR)\
|
||||
({\
|
||||
if((lhs)->dtype == NDARRAY_UINT8) {\
|
||||
if((rhs)->dtype == NDARRAY_UINT8) {\
|
||||
INPLACE_LOOP((lhs), uint8_t, uint8_t, (larray), (rarray), (rstrides), OPERATOR);\
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {\
|
||||
INPLACE_LOOP((lhs), uint8_t, int8_t, (larray), (rarray), (rstrides), OPERATOR);\
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {\
|
||||
INPLACE_LOOP((lhs), uint8_t, uint16_t, (larray), (rarray), (rstrides), OPERATOR);\
|
||||
} else {\
|
||||
INPLACE_LOOP((lhs), uint8_t, int16_t, (larray), (rarray), (rstrides), OPERATOR);\
|
||||
}\
|
||||
} else if(lhs->dtype == NDARRAY_INT8) {\
|
||||
if(rhs->dtype == NDARRAY_UINT8) {\
|
||||
INPLACE_LOOP((lhs), int8_t, uint8_t, (larray), (rarray), (rstrides), OPERATOR);\
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {\
|
||||
INPLACE_LOOP((lhs), int8_t, int8_t, (larray), (rarray), (rstrides), OPERATOR);\
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {\
|
||||
INPLACE_LOOP((lhs), int8_t, uint16_t, (larray), (rarray), (rstrides), OPERATOR);\
|
||||
} else {\
|
||||
INPLACE_LOOP((lhs), int8_t, int16_t, (larray), (rarray), (rstrides), OPERATOR);\
|
||||
}\
|
||||
} else if(lhs->dtype == NDARRAY_UINT16) {\
|
||||
if(rhs->dtype == NDARRAY_UINT8) {\
|
||||
INPLACE_LOOP((lhs), uint16_t, uint8_t, (larray), (rarray), (rstrides), OPERATOR);\
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {\
|
||||
INPLACE_LOOP((lhs), uint16_t, int8_t, (larray), (rarray), (rstrides), OPERATOR);\
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {\
|
||||
INPLACE_LOOP((lhs), uint16_t, uint16_t, (larray), (rarray), (rstrides), OPERATOR);\
|
||||
} else {\
|
||||
INPLACE_LOOP((lhs), uint16_t, int16_t, (larray), (rarray), (rstrides), OPERATOR);\
|
||||
}\
|
||||
} else if(lhs->dtype == NDARRAY_INT16) {\
|
||||
if(rhs->dtype == NDARRAY_UINT8) {\
|
||||
INPLACE_LOOP((lhs), int16_t, uint8_t, (larray), (rarray), (rstrides), OPERATOR);\
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {\
|
||||
INPLACE_LOOP((lhs), int16_t, int8_t, (larray), (rarray), (rstrides), OPERATOR);\
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {\
|
||||
INPLACE_LOOP((lhs), int16_t, uint16_t, (larray), (rarray), (rstrides), OPERATOR);\
|
||||
} else {\
|
||||
INPLACE_LOOP((lhs), int16_t, int16_t, (larray), (rarray), (rstrides), OPERATOR);\
|
||||
}\
|
||||
} else if(lhs->dtype == NDARRAY_FLOAT) {\
|
||||
if(rhs->dtype == NDARRAY_UINT8) {\
|
||||
INPLACE_LOOP((lhs), mp_float_t, uint8_t, (larray), (rarray), (rstrides), OPERATOR);\
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {\
|
||||
INPLACE_LOOP((lhs), mp_float_t, int8_t, (larray), (rarray), (rstrides), OPERATOR);\
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {\
|
||||
INPLACE_LOOP((lhs), mp_float_t, uint16_t, (larray), (rarray), (rstrides), OPERATOR);\
|
||||
} else if(rhs->dtype == NDARRAY_INT16) {\
|
||||
INPLACE_LOOP((lhs), mp_float_t, int16_t, (larray), (rarray), (rstrides), OPERATOR);\
|
||||
} else {\
|
||||
INPLACE_LOOP((lhs), mp_float_t, mp_float_t, (larray), (rarray), (rstrides), OPERATOR);\
|
||||
}\
|
||||
}\
|
||||
})
|
||||
|
||||
#if ULAB_MAX_DIMS == 1
|
||||
#define INPLACE_POWER(results, type_left, type_right, larray, rarray, rstrides)\
|
||||
({ size_t l = 0;\
|
||||
do {\
|
||||
*((type_left *)(larray)) = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
})
|
||||
|
||||
#define FUNC_POINTER_LOOP(results, array, get_lhs, get_rhs, larray, lstrides, rarray, rstrides, OPERATION)\
|
||||
({ size_t l = 0;\
|
||||
do {\
|
||||
mp_float_t lvalue = (get_lhs)((larray));\
|
||||
mp_float_t rvalue = (get_rhs)((rarray));\
|
||||
(set_result)((array), OPERATION);\
|
||||
(array) += (results)->itemsize;\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
})
|
||||
#endif /* ULAB_MAX_DIMS == 1 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 2
|
||||
#define INPLACE_POWER(results, type_left, type_right, larray, rarray, rstrides)\
|
||||
({ size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*((type_left *)(larray)) = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
})
|
||||
|
||||
#define FUNC_POINTER_LOOP(results, array, get_lhs, get_rhs, larray, lstrides, rarray, rstrides, OPERATION)\
|
||||
({ size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
mp_float_t lvalue = (get_lhs)((larray));\
|
||||
mp_float_t rvalue = (get_rhs)((rarray));\
|
||||
(set_result)((array), OPERATION);\
|
||||
(array) += (results)->itemsize;\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < results->shape[ULAB_MAX_DIMS - 2]);\
|
||||
})
|
||||
#endif /* ULAB_MAX_DIMS == 2 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 3
|
||||
#define INPLACE_POWER(results, type_left, type_right, larray, rarray, rstrides)\
|
||||
({ size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*((type_left *)(larray)) = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
})
|
||||
|
||||
|
||||
#define FUNC_POINTER_LOOP(results, array, get_lhs, get_rhs, larray, lstrides, rarray, rstrides, OPERATION)\
|
||||
({ size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
mp_float_t lvalue = (get_lhs)((larray));\
|
||||
mp_float_t rvalue = (get_rhs)((rarray));\
|
||||
(set_result)((array), OPERATION);\
|
||||
(array) += (results)->itemsize;\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < results->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
})
|
||||
#endif /* ULAB_MAX_DIMS == 3 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 4
|
||||
#define INPLACE_POWER(results, type_left, type_right, larray, rarray, rstrides)\
|
||||
({ size_t i = 0;\
|
||||
do {\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*((type_left *)(larray)) = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 4];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
|
||||
i++;\
|
||||
} while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
|
||||
})
|
||||
|
||||
#define FUNC_POINTER_LOOP(results, array, get_lhs, get_rhs, larray, lstrides, rarray, rstrides, OPERATION)\
|
||||
({ size_t i = 0;\
|
||||
do {\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
mp_float_t lvalue = (get_lhs)((larray));\
|
||||
mp_float_t rvalue = (get_rhs)((rarray));\
|
||||
(set_result)((array), OPERATION);\
|
||||
(array) += (results)->itemsize;\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < results->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 4];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
|
||||
i++;\
|
||||
} while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
|
||||
})
|
||||
#endif /* ULAB_MAX_DIMS == 4 */
|
||||
|
||||
#define FLOOR_DIVIDE_UINT1(results, array, type_left, type_right, larray, lstrides, rarray, rstrides)\
|
||||
({\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*(array)++ = *((type_left *)(larray)) / *((type_right *)(rarray));\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
})
|
||||
|
||||
#define FLOOR_DIVIDE1(results, array, type_left, type_right, larray, lstrides, rarray, rstrides)\
|
||||
({\
|
||||
size_t l = 0;\
|
||||
int16_t num;\
|
||||
int16_t denom = (int16_t)*((type_right *)(rarray));\
|
||||
do {\
|
||||
num = (int16_t)*((type_left *)(larray));\
|
||||
if(num >= 0) {\
|
||||
if(denom < 0) {\
|
||||
num += -denom - 1;\
|
||||
}\
|
||||
} else {\
|
||||
if(denom >= 0) {\
|
||||
num += -denom + 1;\
|
||||
}\
|
||||
}\
|
||||
*(array)++ = num / denom;\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
})
|
||||
|
||||
#define FLOOR_DIVIDE_FLOAT1(results, array, type_left, type_right, larray, lstrides, rarray, rstrides)\
|
||||
({\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*(array)++ = MICROPY_FLOAT_C_FUN(floor)(*((type_left *)(larray)) / *((type_right *)(rarray)));\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
})
|
||||
|
||||
#if ULAB_MAX_DIMS == 1
|
||||
#define FLOOR_DIVIDE_LOOP_UINT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
FLOOR_DIVIDE_UINT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
|
||||
} while(0)
|
||||
|
||||
#define FLOOR_DIVIDE_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
FLOOR_DIVIDE1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
|
||||
} while(0)
|
||||
|
||||
#define FLOOR_DIVIDE_LOOP_FLOAT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
FLOOR_DIVIDE_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 1 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 2
|
||||
#define FLOOR_DIVIDE_LOOP_UINT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
FLOOR_DIVIDE_UINT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
} while(0)
|
||||
|
||||
#define FLOOR_DIVIDE_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
FLOOR_DIVIDE1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
} while(0)
|
||||
|
||||
#define FLOOR_DIVIDE_LOOP_FLOAT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
FLOOR_DIVIDE_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
} while(0)
|
||||
|
||||
#endif /* ULAB_MAX_DIMS == 2 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 3
|
||||
#define FLOOR_DIVIDE_LOOP_UINT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
FLOOR_DIVIDE_UINT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
} while(0)
|
||||
|
||||
#define FLOOR_DIVIDE_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
FLOOR_DIVIDE1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
} while(0)
|
||||
|
||||
#define FLOOR_DIVIDE_LOOP_FLOAT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
FLOOR_DIVIDE_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
} while(0)
|
||||
|
||||
#endif /* ULAB_MAX_DIMS == 3 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 4
|
||||
#define FLOOR_DIVIDE_LOOP_UINT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
FLOOR_DIVIDE_UINT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
|
||||
j++;\
|
||||
} while(j < (results)->shape[ULAB_MAX_DIMS - 4]);\
|
||||
} while(0)
|
||||
|
||||
#define FLOOR_DIVIDE_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
FLOOR_DIVIDE1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
|
||||
j++;\
|
||||
} while(j < (results)->shape[ULAB_MAX_DIMS - 4]);\
|
||||
} while(0)
|
||||
|
||||
#define FLOOR_DIVIDE_LOOP_FLOAT(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
FLOOR_DIVIDE_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
|
||||
j++;\
|
||||
} while(j < (results)->shape[ULAB_MAX_DIMS - 4]);\
|
||||
} while(0)
|
||||
|
||||
#endif /* ULAB_MAX_DIMS == 4 */
|
||||
|
||||
#define MODULO_FLOAT1(results, array, type_left, type_right, larray, lstrides, rarray, rstrides)\
|
||||
({\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*(array)++ = MICROPY_FLOAT_C_FUN(fmod)(*((type_left *)(larray)), *((type_right *)(rarray)));\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
})
|
||||
|
||||
#if ULAB_MAX_DIMS == 1
|
||||
#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
MODULO_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 1 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 2
|
||||
#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
MODULO_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 2 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 3
|
||||
#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
MODULO_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 3 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 4
|
||||
#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\
|
||||
type_out *array = (type_out *)(results)->array;\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
MODULO_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
|
||||
j++;\
|
||||
} while(j < (results)->shape[ULAB_MAX_DIMS - 4]);\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 4 */
|
||||
|
||||
|
||||
#define INPLACE_MODULO_FLOAT1(results, type_right, larray, rarray, rstrides)\
|
||||
({\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*((mp_float_t *)larray) = MICROPY_FLOAT_C_FUN(fmod)(*((mp_float_t *)(larray)), *((type_right *)(rarray)));\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
})
|
||||
|
||||
|
||||
#if ULAB_MAX_DIMS == 1
|
||||
#define INPLACE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\
|
||||
INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 1 */
|
||||
|
||||
|
||||
#if ULAB_MAX_DIMS == 2
|
||||
#define INLINE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 2 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 3
|
||||
#define INLINE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 3 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 4
|
||||
#define INLINE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
(larray) -= (results)->strides[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(larray) += (results)->strides[ULAB_MAX_DIMS - 4];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
|
||||
j++;\
|
||||
} while(j < (results)->shape[ULAB_MAX_DIMS - 4]);\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 4 */
|
||||
104
code/ndarray_properties.c
Normal file
104
code/ndarray_properties.c
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021-2025 Zoltán Vörös
|
||||
*
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
|
||||
#include "ulab.h"
|
||||
#include "ndarray.h"
|
||||
#include "numpy/ndarray/ndarray_iter.h"
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
#include "numpy/carray/carray.h"
|
||||
#endif
|
||||
|
||||
void ndarray_properties_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
||||
if (dest[0] == MP_OBJ_NULL) {
|
||||
switch(attr) {
|
||||
#if NDARRAY_HAS_DTYPE
|
||||
case MP_QSTR_dtype:
|
||||
dest[0] = ndarray_dtype(self_in);
|
||||
break;
|
||||
#endif
|
||||
#if NDARRAY_HAS_FLATITER
|
||||
case MP_QSTR_flat:
|
||||
dest[0] = ndarray_flatiter_make_new(self_in);
|
||||
break;
|
||||
#endif
|
||||
#if NDARRAY_HAS_ITEMSIZE
|
||||
case MP_QSTR_itemsize:
|
||||
dest[0] = ndarray_itemsize(self_in);
|
||||
break;
|
||||
#endif
|
||||
#if NDARRAY_HAS_NDIM
|
||||
case MP_QSTR_ndim:
|
||||
dest[0] = ndarray_ndim(self_in);
|
||||
break;
|
||||
#endif
|
||||
#if NDARRAY_HAS_SHAPE
|
||||
case MP_QSTR_shape:
|
||||
dest[0] = ndarray_shape(self_in);
|
||||
break;
|
||||
#endif
|
||||
#if NDARRAY_HAS_SIZE
|
||||
case MP_QSTR_size:
|
||||
dest[0] = ndarray_size(self_in);
|
||||
break;
|
||||
#endif
|
||||
#if NDARRAY_HAS_STRIDES
|
||||
case MP_QSTR_strides:
|
||||
dest[0] = ndarray_strides(self_in);
|
||||
break;
|
||||
#endif
|
||||
#if NDARRAY_HAS_TRANSPOSE
|
||||
case MP_QSTR_T:
|
||||
dest[0] = ndarray_transpose(self_in);
|
||||
break;
|
||||
#endif
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
#if ULAB_NUMPY_HAS_IMAG
|
||||
case MP_QSTR_imag:
|
||||
dest[0] = carray_imag(self_in);
|
||||
break;
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_IMAG
|
||||
case MP_QSTR_real:
|
||||
dest[0] = carray_real(self_in);
|
||||
break;
|
||||
#endif
|
||||
#endif /* ULAB_SUPPORTS_COMPLEX */
|
||||
default:
|
||||
// forward to locals dict
|
||||
dest[1] = MP_OBJ_SENTINEL;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if(dest[1]) {
|
||||
switch(attr) {
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
#if NDARRAY_HAS_RESHAPE
|
||||
case MP_QSTR_shape:
|
||||
ndarray_reshape_core(self_in, dest[1], 1);
|
||||
break;
|
||||
#endif
|
||||
#endif
|
||||
default:
|
||||
return;
|
||||
break;
|
||||
}
|
||||
dest[0] = MP_OBJ_NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
55
code/ndarray_properties.h
Normal file
55
code/ndarray_properties.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
|
||||
* 2020-2025 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _NDARRAY_PROPERTIES_
|
||||
#define _NDARRAY_PROPERTIES_
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/binary.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/objarray.h"
|
||||
|
||||
#include "ulab.h"
|
||||
#include "ndarray.h"
|
||||
#include "numpy/ndarray/ndarray_iter.h"
|
||||
|
||||
void ndarray_properties_attr(mp_obj_t , qstr , mp_obj_t *);
|
||||
|
||||
#if NDARRAY_HAS_DTYPE
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_dtype_obj, ndarray_dtype);
|
||||
#endif
|
||||
|
||||
#if NDARRAY_HAS_FLATITER
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_flatiter_make_new_obj, ndarray_flatiter_make_new);
|
||||
#endif
|
||||
|
||||
#if NDARRAY_HAS_ITEMSIZE
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_itemsize_obj, ndarray_itemsize);
|
||||
#endif
|
||||
|
||||
#if NDARRAY_HAS_NDIM
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_ndim_obj, ndarray_ndim);
|
||||
#endif
|
||||
|
||||
#if NDARRAY_HAS_SHAPE
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_shape_obj, ndarray_shape);
|
||||
#endif
|
||||
|
||||
#if NDARRAY_HAS_SIZE
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_size_obj, ndarray_size);
|
||||
#endif
|
||||
|
||||
#if NDARRAY_HAS_STRIDES
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_strides_obj, ndarray_strides);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
602
code/numerical.c
602
code/numerical.c
|
|
@ -1,602 +0,0 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/builtin.h"
|
||||
#include "py/misc.h"
|
||||
#include "numerical.h"
|
||||
|
||||
enum NUMERICAL_FUNCTION_TYPE {
|
||||
NUMERICAL_MIN,
|
||||
NUMERICAL_MAX,
|
||||
NUMERICAL_ARGMIN,
|
||||
NUMERICAL_ARGMAX,
|
||||
NUMERICAL_SUM,
|
||||
NUMERICAL_MEAN,
|
||||
NUMERICAL_STD,
|
||||
};
|
||||
|
||||
mp_obj_t numerical_linspace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj) } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj) } },
|
||||
{ MP_QSTR_num, MP_ARG_INT, {.u_int = 50} },
|
||||
{ MP_QSTR_endpoint, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_true_obj)} },
|
||||
{ MP_QSTR_retstep, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_false_obj)} },
|
||||
{ MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = NDARRAY_FLOAT} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(2, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
uint16_t len = args[2].u_int;
|
||||
if(len < 2) {
|
||||
mp_raise_ValueError("number of points must be at least 2");
|
||||
}
|
||||
mp_float_t value, step;
|
||||
value = mp_obj_get_float(args[0].u_obj);
|
||||
uint8_t typecode = args[5].u_int;
|
||||
if(args[3].u_obj == mp_const_true) step = (mp_obj_get_float(args[1].u_obj)-value)/(len-1);
|
||||
else step = (mp_obj_get_float(args[1].u_obj)-value)/len;
|
||||
ndarray_obj_t *ndarray = create_new_ndarray(1, len, typecode);
|
||||
if(typecode == NDARRAY_UINT8) {
|
||||
uint8_t *array = (uint8_t *)ndarray->array->items;
|
||||
for(size_t i=0; i < len; i++, value += step) array[i] = (uint8_t)value;
|
||||
} else if(typecode == NDARRAY_INT8) {
|
||||
int8_t *array = (int8_t *)ndarray->array->items;
|
||||
for(size_t i=0; i < len; i++, value += step) array[i] = (int8_t)value;
|
||||
} else if(typecode == NDARRAY_UINT16) {
|
||||
uint16_t *array = (uint16_t *)ndarray->array->items;
|
||||
for(size_t i=0; i < len; i++, value += step) array[i] = (uint16_t)value;
|
||||
} else if(typecode == NDARRAY_INT16) {
|
||||
int16_t *array = (int16_t *)ndarray->array->items;
|
||||
for(size_t i=0; i < len; i++, value += step) array[i] = (int16_t)value;
|
||||
} else {
|
||||
mp_float_t *array = (mp_float_t *)ndarray->array->items;
|
||||
for(size_t i=0; i < len; i++, value += step) array[i] = value;
|
||||
}
|
||||
if(args[4].u_obj == mp_const_false) {
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
} else {
|
||||
mp_obj_t tuple[2];
|
||||
tuple[0] = ndarray;
|
||||
tuple[1] = mp_obj_new_float(step);
|
||||
return mp_obj_new_tuple(2, tuple);
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_t numerical_sum_mean_std_array(mp_obj_t oin, uint8_t optype) {
|
||||
mp_float_t value, sum = 0.0, sq_sum = 0.0;
|
||||
mp_obj_iter_buf_t iter_buf;
|
||||
mp_obj_t item, iterable = mp_getiter(oin, &iter_buf);
|
||||
mp_int_t len = mp_obj_get_int(mp_obj_len(oin));
|
||||
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
value = mp_obj_get_float(item);
|
||||
sum += value;
|
||||
if(optype == NUMERICAL_STD) {
|
||||
sq_sum += value*value;
|
||||
}
|
||||
}
|
||||
if(optype == NUMERICAL_SUM) {
|
||||
return mp_obj_new_float(sum);
|
||||
} else if(optype == NUMERICAL_MEAN) {
|
||||
return mp_obj_new_float(sum/len);
|
||||
} else {
|
||||
sum /= len; // this is now the mean!
|
||||
return mp_obj_new_float(MICROPY_FLOAT_C_FUN(sqrt)(sq_sum/len-sum*sum));
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_float_t numerical_sum_mean_std_single_line(void *data, size_t start, size_t stop,
|
||||
size_t stride, uint8_t typecode, uint8_t optype) {
|
||||
|
||||
mp_float_t sum = 0.0, sq_sum = 0.0, value;
|
||||
size_t len = 0;
|
||||
for(size_t i=start; i < stop; i+=stride, len++) {
|
||||
value = ndarray_get_float_value(data, typecode, i);
|
||||
sum += value;
|
||||
if(optype == NUMERICAL_STD) {
|
||||
sq_sum += value*value;
|
||||
}
|
||||
}
|
||||
if(len == 0) {
|
||||
mp_raise_ValueError("data length is 0!");
|
||||
}
|
||||
if(optype == NUMERICAL_SUM) {
|
||||
return sum;
|
||||
} else if(optype == NUMERICAL_MEAN) {
|
||||
return sum/len;
|
||||
} else {
|
||||
sum /= len; // this is now the mean!
|
||||
return MICROPY_FLOAT_C_FUN(sqrt)(sq_sum/len-sum*sum);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t numerical_sum_mean_std_matrix(mp_obj_t oin, mp_obj_t axis, uint8_t optype) {
|
||||
ndarray_obj_t *in = MP_OBJ_TO_PTR(oin);
|
||||
if((axis == mp_const_none) || (in->m == 1) || (in->n == 1)) {
|
||||
// return the value for the flattened array
|
||||
return mp_obj_new_float(numerical_sum_mean_std_single_line(in->array->items, 0,
|
||||
in->array->len, 1, in->array->typecode, optype));
|
||||
} else {
|
||||
uint8_t _axis = mp_obj_get_int(axis);
|
||||
size_t m = (_axis == 0) ? 1 : in->m;
|
||||
size_t n = (_axis == 0) ? in->n : 1;
|
||||
size_t len = in->array->len;
|
||||
mp_float_t sms;
|
||||
// TODO: pass in->array->typcode to create_new_ndarray
|
||||
ndarray_obj_t *out = create_new_ndarray(m, n, NDARRAY_FLOAT);
|
||||
|
||||
// TODO: these two cases could probably be combined in a more elegant fashion...
|
||||
if(_axis == 0) { // vertical
|
||||
for(size_t i=0; i < n; i++) {
|
||||
sms = numerical_sum_mean_std_single_line(in->array->items, i, len,
|
||||
n, in->array->typecode, optype);
|
||||
((float_t *)out->array->items)[i] = sms;
|
||||
}
|
||||
} else { // horizontal
|
||||
for(size_t i=0; i < m; i++) {
|
||||
sms = numerical_sum_mean_std_single_line(in->array->items, i*in->n,
|
||||
(i+1)*in->n, 1, in->array->typecode, optype);
|
||||
((float_t *)out->array->items)[i] = sms;
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(out);
|
||||
}
|
||||
}
|
||||
|
||||
size_t numerical_argmin_argmax_array(ndarray_obj_t *in, size_t start,
|
||||
size_t stop, size_t stride, uint8_t op) {
|
||||
size_t best_idx = start;
|
||||
if(in->array->typecode == NDARRAY_UINT8) {
|
||||
ARG_MIN_LOOP(in, uint8_t, start, stop, stride, op);
|
||||
} else if(in->array->typecode == NDARRAY_INT8) {
|
||||
ARG_MIN_LOOP(in, int8_t, start, stop, stride, op);
|
||||
} else if(in->array->typecode == NDARRAY_UINT16) {
|
||||
ARG_MIN_LOOP(in, uint16_t, start, stop, stride, op);
|
||||
} else if(in->array->typecode == NDARRAY_INT16) {
|
||||
ARG_MIN_LOOP(in, uint16_t, start, stop, stride, op);
|
||||
} else if(in->array->typecode == NDARRAY_FLOAT) {
|
||||
ARG_MIN_LOOP(in, mp_float_t, start, stop, stride, op);
|
||||
}
|
||||
return best_idx;
|
||||
}
|
||||
|
||||
void copy_value_into_ndarray(ndarray_obj_t *target, ndarray_obj_t *source, size_t target_idx, size_t source_idx) {
|
||||
// since we are simply copying, it doesn't matter, whether the arrays are signed or unsigned,
|
||||
// we can cast them in any way we like
|
||||
// This could also be done with byte copies. I don't know, whether that would have any benefits
|
||||
if((target->array->typecode == NDARRAY_UINT8) || (target->array->typecode == NDARRAY_INT8)) {
|
||||
((uint8_t *)target->array->items)[target_idx] = ((uint8_t *)source->array->items)[source_idx];
|
||||
} else if((target->array->typecode == NDARRAY_UINT16) || (target->array->typecode == NDARRAY_INT16)) {
|
||||
((uint16_t *)target->array->items)[target_idx] = ((uint16_t *)source->array->items)[source_idx];
|
||||
} else {
|
||||
((float *)target->array->items)[target_idx] = ((float *)source->array->items)[source_idx];
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t numerical_argmin_argmax(mp_obj_t oin, mp_obj_t axis, uint8_t optype) {
|
||||
if(MP_OBJ_IS_TYPE(oin, &mp_type_tuple) || MP_OBJ_IS_TYPE(oin, &mp_type_list) ||
|
||||
MP_OBJ_IS_TYPE(oin, &mp_type_range)) {
|
||||
// This case will work for single iterables only
|
||||
size_t idx = 0, best_idx = 0;
|
||||
mp_obj_iter_buf_t iter_buf;
|
||||
mp_obj_t iterable = mp_getiter(oin, &iter_buf);
|
||||
mp_obj_t best_obj = MP_OBJ_NULL;
|
||||
mp_obj_t item;
|
||||
mp_uint_t op = MP_BINARY_OP_LESS;
|
||||
if((optype == NUMERICAL_ARGMAX) || (optype == NUMERICAL_MAX)) op = MP_BINARY_OP_MORE;
|
||||
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
if ((best_obj == MP_OBJ_NULL) || (mp_binary_op(op, item, best_obj) == mp_const_true)) {
|
||||
best_obj = item;
|
||||
best_idx = idx;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
if((optype == NUMERICAL_ARGMIN) || (optype == NUMERICAL_ARGMAX)) {
|
||||
return MP_OBJ_NEW_SMALL_INT(best_idx);
|
||||
} else {
|
||||
return best_obj;
|
||||
}
|
||||
} else if(mp_obj_is_type(oin, &ulab_ndarray_type)) {
|
||||
ndarray_obj_t *in = MP_OBJ_TO_PTR(oin);
|
||||
size_t best_idx;
|
||||
if((axis == mp_const_none) || (in->m == 1) || (in->n == 1)) {
|
||||
// return the value for the flattened array
|
||||
best_idx = numerical_argmin_argmax_array(in, 0, in->array->len, 1, optype);
|
||||
if((optype == NUMERICAL_ARGMIN) || (optype == NUMERICAL_ARGMAX)) {
|
||||
return MP_OBJ_NEW_SMALL_INT(best_idx);
|
||||
} else {
|
||||
if(in->array->typecode == NDARRAY_FLOAT) {
|
||||
return mp_obj_new_float(ndarray_get_float_value(in->array->items, in->array->typecode, best_idx));
|
||||
} else {
|
||||
return mp_binary_get_val_array(in->array->typecode, in->array->items, best_idx);
|
||||
}
|
||||
}
|
||||
} else { // we have to work with a full matrix here
|
||||
uint8_t _axis = mp_obj_get_int(axis);
|
||||
size_t m = (_axis == 0) ? 1 : in->m;
|
||||
size_t n = (_axis == 0) ? in->n : 1;
|
||||
size_t len = in->array->len;
|
||||
ndarray_obj_t *ndarray = NULL;
|
||||
if((optype == NUMERICAL_MAX) || (optype == NUMERICAL_MIN)) {
|
||||
ndarray = create_new_ndarray(m, n, in->array->typecode);
|
||||
} else { // argmin/argmax
|
||||
// TODO: one might get away with uint8_t, if both m, and n < 255
|
||||
ndarray = create_new_ndarray(m, n, NDARRAY_UINT16);
|
||||
}
|
||||
|
||||
// TODO: these two cases could probably be combined in a more elegant fashion...
|
||||
if(_axis == 0) { // vertical
|
||||
for(size_t i=0; i < n; i++) {
|
||||
best_idx = numerical_argmin_argmax_array(in, i, len, n, optype);
|
||||
if((optype == NUMERICAL_MIN) || (optype == NUMERICAL_MAX)) {
|
||||
copy_value_into_ndarray(ndarray, in, i, best_idx);
|
||||
} else {
|
||||
((uint16_t *)ndarray->array->items)[i] = (uint16_t)(best_idx / n);
|
||||
}
|
||||
}
|
||||
} else { // horizontal
|
||||
for(size_t i=0; i < m; i++) {
|
||||
best_idx = numerical_argmin_argmax_array(in, i*in->n, (i+1)*in->n, 1, optype);
|
||||
if((optype == NUMERICAL_MIN) || (optype == NUMERICAL_MAX)) {
|
||||
copy_value_into_ndarray(ndarray, in, i, best_idx);
|
||||
} else {
|
||||
((uint16_t *)ndarray->array->items)[i] = (uint16_t)(best_idx - i*in->n);
|
||||
}
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
mp_raise_TypeError("input type is not supported");
|
||||
}
|
||||
|
||||
STATIC mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, uint8_t type) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} } ,
|
||||
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(1, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
mp_obj_t oin = args[0].u_obj;
|
||||
mp_obj_t axis = args[1].u_obj;
|
||||
if((axis != mp_const_none) && (mp_obj_get_int(axis) != 0) && (mp_obj_get_int(axis) != 1)) {
|
||||
// this seems to pass with False, and True...
|
||||
mp_raise_ValueError("axis must be None, 0, or 1");
|
||||
}
|
||||
|
||||
if(MP_OBJ_IS_TYPE(oin, &mp_type_tuple) || MP_OBJ_IS_TYPE(oin, &mp_type_list) ||
|
||||
MP_OBJ_IS_TYPE(oin, &mp_type_range)) {
|
||||
switch(type) {
|
||||
case NUMERICAL_MIN:
|
||||
case NUMERICAL_ARGMIN:
|
||||
case NUMERICAL_MAX:
|
||||
case NUMERICAL_ARGMAX:
|
||||
return numerical_argmin_argmax(oin, axis, type);
|
||||
case NUMERICAL_SUM:
|
||||
case NUMERICAL_MEAN:
|
||||
case NUMERICAL_STD:
|
||||
return numerical_sum_mean_std_array(oin, type);
|
||||
default: // we should never reach this point, but whatever
|
||||
return mp_const_none;
|
||||
}
|
||||
} else if(MP_OBJ_IS_TYPE(oin, &ulab_ndarray_type)) {
|
||||
switch(type) {
|
||||
case NUMERICAL_MIN:
|
||||
case NUMERICAL_MAX:
|
||||
case NUMERICAL_ARGMIN:
|
||||
case NUMERICAL_ARGMAX:
|
||||
return numerical_argmin_argmax(oin, axis, type);
|
||||
case NUMERICAL_SUM:
|
||||
case NUMERICAL_MEAN:
|
||||
case NUMERICAL_STD:
|
||||
return numerical_sum_mean_std_matrix(oin, axis, type);
|
||||
default:
|
||||
mp_raise_NotImplementedError("operation is not implemented on ndarrays");
|
||||
}
|
||||
} else {
|
||||
mp_raise_TypeError("input must be tuple, list, range, or ndarray");
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t numerical_min(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return numerical_function(n_args, pos_args, kw_args, NUMERICAL_MIN);
|
||||
}
|
||||
|
||||
mp_obj_t numerical_max(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return numerical_function(n_args, pos_args, kw_args, NUMERICAL_MAX);
|
||||
}
|
||||
|
||||
mp_obj_t numerical_argmin(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return numerical_function(n_args, pos_args, kw_args, NUMERICAL_ARGMIN);
|
||||
}
|
||||
|
||||
mp_obj_t numerical_argmax(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return numerical_function(n_args, pos_args, kw_args, NUMERICAL_ARGMAX);
|
||||
}
|
||||
|
||||
mp_obj_t numerical_sum(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return numerical_function(n_args, pos_args, kw_args, NUMERICAL_SUM);
|
||||
}
|
||||
|
||||
mp_obj_t numerical_mean(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return numerical_function(n_args, pos_args, kw_args, NUMERICAL_MEAN);
|
||||
}
|
||||
|
||||
mp_obj_t numerical_std(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return numerical_function(n_args, pos_args, kw_args, NUMERICAL_STD);
|
||||
}
|
||||
|
||||
mp_obj_t numerical_roll(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj) } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj) } },
|
||||
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(2, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
mp_obj_t oin = args[0].u_obj;
|
||||
int16_t shift = mp_obj_get_int(args[1].u_obj);
|
||||
if((args[2].u_obj != mp_const_none) &&
|
||||
(mp_obj_get_int(args[2].u_obj) != 0) &&
|
||||
(mp_obj_get_int(args[2].u_obj) != 1)) {
|
||||
mp_raise_ValueError("axis must be None, 0, or 1");
|
||||
}
|
||||
|
||||
ndarray_obj_t *in = MP_OBJ_TO_PTR(oin);
|
||||
uint8_t _sizeof = mp_binary_get_size('@', in->array->typecode, NULL);
|
||||
size_t len;
|
||||
int16_t _shift;
|
||||
uint8_t *array = (uint8_t *)in->array->items;
|
||||
// TODO: transpose the matrix, if axis == 0. Though, that is hard on the RAM...
|
||||
if(shift < 0) {
|
||||
_shift = -shift;
|
||||
} else {
|
||||
_shift = shift;
|
||||
}
|
||||
if((args[2].u_obj == mp_const_none) || (mp_obj_get_int(args[2].u_obj) == 1)) { // shift horizontally
|
||||
uint16_t M;
|
||||
if(args[2].u_obj == mp_const_none) {
|
||||
len = in->array->len;
|
||||
M = 1;
|
||||
} else {
|
||||
len = in->n;
|
||||
M = in->m;
|
||||
}
|
||||
_shift = _shift % len;
|
||||
if(shift < 0) _shift = len - _shift;
|
||||
// TODO: if(shift > len/2), we should move in the opposite direction. That would save RAM
|
||||
_shift *= _sizeof;
|
||||
uint8_t *tmp = m_new(uint8_t, _shift);
|
||||
for(size_t m=0; m < M; m++) {
|
||||
memmove(tmp, &array[m*len*_sizeof], _shift);
|
||||
memmove(&array[m*len*_sizeof], &array[m*len*_sizeof+_shift], len*_sizeof-_shift);
|
||||
memmove(&array[(m+1)*len*_sizeof-_shift], tmp, _shift);
|
||||
}
|
||||
m_del(uint8_t, tmp, _shift);
|
||||
return mp_const_none;
|
||||
} else {
|
||||
len = in->m;
|
||||
// temporary buffer
|
||||
uint8_t *_data = m_new(uint8_t, _sizeof*len);
|
||||
|
||||
_shift = _shift % len;
|
||||
if(shift < 0) _shift = len - _shift;
|
||||
_shift *= _sizeof;
|
||||
uint8_t *tmp = m_new(uint8_t, _shift);
|
||||
|
||||
for(size_t n=0; n < in->n; n++) {
|
||||
for(size_t m=0; m < len; m++) {
|
||||
// this loop should fill up the temporary buffer
|
||||
memmove(&_data[m*_sizeof], &array[(m*in->n+n)*_sizeof], _sizeof);
|
||||
}
|
||||
// now, the actual shift
|
||||
memmove(tmp, _data, _shift);
|
||||
memmove(_data, &_data[_shift], len*_sizeof-_shift);
|
||||
memmove(&_data[len*_sizeof-_shift], tmp, _shift);
|
||||
for(size_t m=0; m < len; m++) {
|
||||
// this loop should dump the content of the temporary buffer into data
|
||||
memmove(&array[(m*in->n+n)*_sizeof], &_data[m*_sizeof], _sizeof);
|
||||
}
|
||||
}
|
||||
m_del(uint8_t, tmp, _shift);
|
||||
m_del(uint8_t, _data, _sizeof*len);
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_t numerical_flip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj) } },
|
||||
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(1, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError("flip argument must be an ndarray");
|
||||
}
|
||||
if((args[1].u_obj != mp_const_none) &&
|
||||
(mp_obj_get_int(args[1].u_obj) != 0) &&
|
||||
(mp_obj_get_int(args[1].u_obj) != 1)) {
|
||||
mp_raise_ValueError("axis must be None, 0, or 1");
|
||||
}
|
||||
|
||||
ndarray_obj_t *in = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
mp_obj_t oout = ndarray_copy(args[0].u_obj);
|
||||
ndarray_obj_t *out = MP_OBJ_TO_PTR(oout);
|
||||
uint8_t _sizeof = mp_binary_get_size('@', in->array->typecode, NULL);
|
||||
uint8_t *array_in = (uint8_t *)in->array->items;
|
||||
uint8_t *array_out = (uint8_t *)out->array->items;
|
||||
size_t len;
|
||||
if((args[1].u_obj == mp_const_none) || (mp_obj_get_int(args[1].u_obj) == 1)) { // flip horizontally
|
||||
uint16_t M = in->m;
|
||||
len = in->n;
|
||||
if(args[1].u_obj == mp_const_none) { // flip flattened array
|
||||
len = in->array->len;
|
||||
M = 1;
|
||||
}
|
||||
for(size_t m=0; m < M; m++) {
|
||||
for(size_t n=0; n < len; n++) {
|
||||
memcpy(array_out+_sizeof*(m*len+n), array_in+_sizeof*((m+1)*len-n-1), _sizeof);
|
||||
}
|
||||
}
|
||||
} else { // flip vertically
|
||||
for(size_t m=0; m < in->m; m++) {
|
||||
for(size_t n=0; n < in->n; n++) {
|
||||
memcpy(array_out+_sizeof*(m*in->n+n), array_in+_sizeof*((in->m-m-1)*in->n+n), _sizeof);
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
mp_obj_t numerical_diff(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj) } },
|
||||
{ MP_QSTR_n, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1 } },
|
||||
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1 } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(1, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError("diff argument must be an ndarray");
|
||||
}
|
||||
|
||||
ndarray_obj_t *in = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
size_t increment, N, M;
|
||||
if((args[2].u_int == -1) || (args[2].u_int == 1)) { // differentiate along the horizontal axis
|
||||
increment = 1;
|
||||
} else if(args[2].u_int == 0) { // differtiate along vertical axis
|
||||
increment = in->n;
|
||||
} else {
|
||||
mp_raise_ValueError("axis must be -1, 0, or 1");
|
||||
}
|
||||
if((args[1].u_int < 0) || (args[1].u_int > 9)) {
|
||||
mp_raise_ValueError("n must be between 0, and 9");
|
||||
}
|
||||
uint8_t n = args[1].u_int;
|
||||
int8_t *stencil = m_new(int8_t, n+1);
|
||||
stencil[0] = 1;
|
||||
for(uint8_t i=1; i < n+1; i++) {
|
||||
stencil[i] = -stencil[i-1]*(n-i+1)/i;
|
||||
}
|
||||
|
||||
ndarray_obj_t *out;
|
||||
|
||||
if(increment == 1) { // differentiate along the horizontal axis
|
||||
if(n >= in->n) {
|
||||
out = create_new_ndarray(in->m, 0, in->array->typecode);
|
||||
m_del(uint8_t, stencil, n);
|
||||
return MP_OBJ_FROM_PTR(out);
|
||||
}
|
||||
N = in->n - n;
|
||||
M = in->m;
|
||||
} else { // differentiate along vertical axis
|
||||
if(n >= in->m) {
|
||||
out = create_new_ndarray(0, in->n, in->array->typecode);
|
||||
m_del(uint8_t, stencil, n);
|
||||
return MP_OBJ_FROM_PTR(out);
|
||||
}
|
||||
M = in->m - n;
|
||||
N = in->n;
|
||||
}
|
||||
out = create_new_ndarray(M, N, in->array->typecode);
|
||||
if(in->array->typecode == NDARRAY_UINT8) {
|
||||
CALCULATE_DIFF(in, out, uint8_t, M, N, in->n, increment);
|
||||
} else if(in->array->typecode == NDARRAY_INT8) {
|
||||
CALCULATE_DIFF(in, out, int8_t, M, N, in->n, increment);
|
||||
} else if(in->array->typecode == NDARRAY_UINT16) {
|
||||
CALCULATE_DIFF(in, out, uint16_t, M, N, in->n, increment);
|
||||
} else if(in->array->typecode == NDARRAY_INT16) {
|
||||
CALCULATE_DIFF(in, out, int16_t, M, N, in->n, increment);
|
||||
} else {
|
||||
CALCULATE_DIFF(in, out, mp_float_t, M, N, in->n, increment);
|
||||
}
|
||||
m_del(int8_t, stencil, n);
|
||||
return MP_OBJ_FROM_PTR(out);
|
||||
}
|
||||
/*
|
||||
void heapsort(ndarray_obj_t *ndarray, size_t n, size_t increment) {
|
||||
uint8_t type;
|
||||
if((ndarray->array->typecode == NDARRAY_UINT8) || (ndarray->array->typecode == NDARRAY_INT8)) {
|
||||
type = 0;
|
||||
}
|
||||
if((ndarray->array->typecode == NDARRAY_UINT16) || (ndarray->array->typecode == NDARRAY_INT16)) {
|
||||
type = 1;
|
||||
} else {
|
||||
type = 2;
|
||||
}
|
||||
|
||||
size_t i, j, k, l;
|
||||
l = (n >> 1) + 1;
|
||||
k = n;
|
||||
uint8_t ui8_tmp, *ui8_array = NULL;
|
||||
uint16_t ui16_tmp, *ui16_array = NULL;
|
||||
mp_float_t float_tmp, *float_array = NULL;
|
||||
|
||||
for(;;) {
|
||||
if (l > 1) {
|
||||
if((ndarray->array->typecode == NDARRAY_UINT8) || (ndarray->array->typecode == NDARRAY_INT8)) {
|
||||
ui8_tmp = ui8_array[--l];
|
||||
}
|
||||
} else {
|
||||
tmp = array[k];
|
||||
array[k] = array[1];
|
||||
if (--k == 1) {
|
||||
array[1] = tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
i = l;
|
||||
j = 2 * l;
|
||||
while(j <= k) {
|
||||
if(j < k && array[j] < array[j+1]) {
|
||||
j++;
|
||||
}
|
||||
}
|
||||
if(tmp < array[j]) {
|
||||
array[i] = array[j];
|
||||
i = j;
|
||||
j <<= 1;
|
||||
} else break;
|
||||
}
|
||||
array[i] = tmp;
|
||||
}
|
||||
|
||||
mp_obj_t numerical_sort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj) } },
|
||||
{ MP_QSTR_n, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1 } },
|
||||
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1 } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(1, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError("sort argument must be an ndarray");
|
||||
}
|
||||
return mp_const_none;
|
||||
}*/
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _NUMERICAL_
|
||||
#define _NUMERICAL_
|
||||
|
||||
#include "ndarray.h"
|
||||
|
||||
mp_obj_t numerical_linspace(size_t , const mp_obj_t *, mp_map_t *);
|
||||
mp_obj_t numerical_sum(size_t , const mp_obj_t *, mp_map_t *);
|
||||
mp_obj_t numerical_mean(size_t , const mp_obj_t *, mp_map_t *);
|
||||
mp_obj_t numerical_std(size_t , const mp_obj_t *, mp_map_t *);
|
||||
mp_obj_t numerical_min(size_t , const mp_obj_t *, mp_map_t *);
|
||||
mp_obj_t numerical_max(size_t , const mp_obj_t *, mp_map_t *);
|
||||
mp_obj_t numerical_argmin(size_t , const mp_obj_t *, mp_map_t *);
|
||||
mp_obj_t numerical_argmax(size_t , const mp_obj_t *, mp_map_t *);
|
||||
mp_obj_t numerical_roll(size_t , const mp_obj_t *, mp_map_t *);
|
||||
|
||||
// TODO: implement minimum/maximum, and cumsum
|
||||
mp_obj_t numerical_minimum(mp_obj_t , mp_obj_t );
|
||||
mp_obj_t numerical_maximum(mp_obj_t , mp_obj_t );
|
||||
mp_obj_t numerical_cumsum(size_t , const mp_obj_t *, mp_map_t *);
|
||||
mp_obj_t numerical_flip(size_t , const mp_obj_t *, mp_map_t *);
|
||||
mp_obj_t numerical_diff(size_t , const mp_obj_t *, mp_map_t *);
|
||||
mp_obj_t numerical_sort(size_t , const mp_obj_t *, mp_map_t *);
|
||||
|
||||
// this macro could be tighter, if we moved the ifs to the argmin function, assigned <, as well as >
|
||||
#define ARG_MIN_LOOP(in, type, start, stop, stride, op) do {\
|
||||
type *array = (type *)(in)->array->items;\
|
||||
if(((op) == NUMERICAL_MAX) || ((op) == NUMERICAL_ARGMAX)) {\
|
||||
for(size_t i=(start)+(stride); i < (stop); i+=(stride)) {\
|
||||
if((array)[i] > (array)[best_idx]) {\
|
||||
best_idx = i;\
|
||||
}\
|
||||
}\
|
||||
} else{\
|
||||
for(size_t i=(start)+(stride); i < (stop); i+=(stride)) {\
|
||||
if((array)[i] < (array)[best_idx]) best_idx = i;\
|
||||
}\
|
||||
}\
|
||||
} while(0)
|
||||
|
||||
|
||||
#define CALCULATE_DIFF(in, out, type, M, N, inn, increment) do {\
|
||||
type *source = (type *)(in)->array->items;\
|
||||
type *target = (type *)(out)->array->items;\
|
||||
for(size_t i=0; i < (M); i++) {\
|
||||
for(size_t j=0; j < (N); j++) {\
|
||||
for(uint8_t k=0; k < n+1; k++) {\
|
||||
target[i*(N)+j] -= stencil[k]*source[i*(inn)+j+k*(increment)];\
|
||||
}\
|
||||
}\
|
||||
}\
|
||||
} while(0)
|
||||
|
||||
#endif
|
||||
227
code/numpy/approx.c
Normal file
227
code/numpy/approx.c
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2021 Zoltán Vörös
|
||||
* 2020 Diego Elio Pettenò
|
||||
* 2020 Taku Fukada
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "../ulab_tools.h"
|
||||
#include "carray/carray_tools.h"
|
||||
#include "approx.h"
|
||||
|
||||
//| """Numerical approximation methods"""
|
||||
//|
|
||||
|
||||
ULAB_DEFINE_FLOAT_CONST(approx_trapz_dx, MICROPY_FLOAT_CONST(1.0), 0x3f800000UL, 0x3ff0000000000000ULL);
|
||||
|
||||
#if ULAB_NUMPY_HAS_INTERP
|
||||
//| def interp(
|
||||
//| x: ulab.numpy.ndarray,
|
||||
//| xp: ulab.numpy.ndarray,
|
||||
//| fp: ulab.numpy.ndarray,
|
||||
//| *,
|
||||
//| left: Optional[_float] = None,
|
||||
//| right: Optional[_float] = None
|
||||
//| ) -> ulab.numpy.ndarray:
|
||||
//| """
|
||||
//| :param ulab.numpy.ndarray x: The x-coordinates at which to evaluate the interpolated values.
|
||||
//| :param ulab.numpy.ndarray xp: The x-coordinates of the data points, must be increasing
|
||||
//| :param ulab.numpy.ndarray fp: The y-coordinates of the data points, same length as xp
|
||||
//| :param left: Value to return for ``x < xp[0]``, default is ``fp[0]``.
|
||||
//| :param right: Value to return for ``x > xp[-1]``, default is ``fp[-1]``.
|
||||
//|
|
||||
//| Returns the one-dimensional piecewise linear interpolant to a function with given discrete data points (xp, fp), evaluated at x."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t approx_interp(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_left, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
||||
{ MP_QSTR_right, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
||||
};
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
ndarray_obj_t *x = ndarray_from_mp_obj(args[0].u_obj, 0);
|
||||
ndarray_obj_t *xp = ndarray_from_mp_obj(args[1].u_obj, 0); // xp must hold an increasing sequence of independent values
|
||||
ndarray_obj_t *fp = ndarray_from_mp_obj(args[2].u_obj, 0);
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(x->dtype)
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(xp->dtype)
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(fp->dtype)
|
||||
if((xp->ndim != 1) || (fp->ndim != 1) || (xp->len < 2) || (fp->len < 2) || (xp->len != fp->len)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("interp is defined for 1D iterables of equal length"));
|
||||
}
|
||||
|
||||
ndarray_obj_t *y = ndarray_new_linear_array(x->len, NDARRAY_FLOAT);
|
||||
mp_float_t left_value, right_value;
|
||||
uint8_t *xparray = (uint8_t *)xp->array;
|
||||
|
||||
mp_float_t xp_left = ndarray_get_float_value(xparray, xp->dtype);
|
||||
xparray += (xp->len-1) * xp->strides[ULAB_MAX_DIMS - 1];
|
||||
mp_float_t xp_right = ndarray_get_float_value(xparray, xp->dtype);
|
||||
|
||||
uint8_t *fparray = (uint8_t *)fp->array;
|
||||
|
||||
if(args[3].u_obj == mp_const_none) {
|
||||
left_value = ndarray_get_float_value(fparray, fp->dtype);
|
||||
} else {
|
||||
left_value = mp_obj_get_float(args[3].u_obj);
|
||||
}
|
||||
if(args[4].u_obj == mp_const_none) {
|
||||
fparray += (fp->len-1) * fp->strides[ULAB_MAX_DIMS - 1];
|
||||
right_value = ndarray_get_float_value(fparray, fp->dtype);
|
||||
} else {
|
||||
right_value = mp_obj_get_float(args[4].u_obj);
|
||||
}
|
||||
|
||||
xparray = xp->array;
|
||||
fparray = fp->array;
|
||||
|
||||
uint8_t *xarray = (uint8_t *)x->array;
|
||||
mp_float_t *yarray = (mp_float_t *)y->array;
|
||||
uint8_t *temp;
|
||||
|
||||
for(size_t i=0; i < x->len; i++, yarray++) {
|
||||
mp_float_t x_value = ndarray_get_float_value(xarray, x->dtype);
|
||||
xarray += x->strides[ULAB_MAX_DIMS - 1];
|
||||
if(x_value < xp_left) {
|
||||
*yarray = left_value;
|
||||
} else if(x_value > xp_right) {
|
||||
*yarray = right_value;
|
||||
} else { // do the binary search here
|
||||
mp_float_t xp_left_, xp_right_;
|
||||
mp_float_t fp_left, fp_right;
|
||||
size_t left_index = 0, right_index = xp->len - 1, middle_index;
|
||||
while(right_index - left_index > 1) {
|
||||
middle_index = left_index + (right_index - left_index) / 2;
|
||||
temp = xparray + middle_index * xp->strides[ULAB_MAX_DIMS - 1];
|
||||
mp_float_t xp_middle = ndarray_get_float_value(temp, xp->dtype);
|
||||
if(x_value <= xp_middle) {
|
||||
right_index = middle_index;
|
||||
} else {
|
||||
left_index = middle_index;
|
||||
}
|
||||
}
|
||||
temp = xparray + left_index * xp->strides[ULAB_MAX_DIMS - 1];
|
||||
xp_left_ = ndarray_get_float_value(temp, xp->dtype);
|
||||
|
||||
temp = xparray + right_index * xp->strides[ULAB_MAX_DIMS - 1];
|
||||
xp_right_ = ndarray_get_float_value(temp, xp->dtype);
|
||||
|
||||
temp = fparray + left_index * fp->strides[ULAB_MAX_DIMS - 1];
|
||||
fp_left = ndarray_get_float_value(temp, fp->dtype);
|
||||
|
||||
temp = fparray + right_index * fp->strides[ULAB_MAX_DIMS - 1];
|
||||
fp_right = ndarray_get_float_value(temp, fp->dtype);
|
||||
|
||||
*yarray = fp_left + (x_value - xp_left_) * (fp_right - fp_left) / (xp_right_ - xp_left_);
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(y);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(approx_interp_obj, 2, approx_interp);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_TRAPZ
|
||||
//| def trapz(y: ulab.numpy.ndarray, x: Optional[ulab.numpy.ndarray] = None, dx: _float = 1.0) -> _float:
|
||||
//| """
|
||||
//| :param 1D ulab.numpy.ndarray y: the values of the dependent variable
|
||||
//| :param 1D ulab.numpy.ndarray x: optional, the coordinates of the independent variable. Defaults to uniformly spaced values.
|
||||
//| :param float dx: the spacing between sample points, if x=None
|
||||
//|
|
||||
//| Returns the integral of y(x) using the trapezoidal rule.
|
||||
//| """
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t approx_trapz(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_x, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_dx, MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(approx_trapz_dx)} },
|
||||
};
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
ndarray_obj_t *y = ndarray_from_mp_obj(args[0].u_obj, 0);
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(y->dtype)
|
||||
ndarray_obj_t *x;
|
||||
mp_float_t mean = MICROPY_FLOAT_CONST(0.0);
|
||||
if(y->len < 2) {
|
||||
return mp_obj_new_float(mean);
|
||||
}
|
||||
if((y->ndim != 1)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("trapz is defined for 1D iterables"));
|
||||
}
|
||||
|
||||
mp_float_t (*funcy)(void *) = ndarray_get_float_function(y->dtype);
|
||||
uint8_t *yarray = (uint8_t *)y->array;
|
||||
|
||||
size_t count = 1;
|
||||
mp_float_t y1, y2, m;
|
||||
|
||||
if(args[1].u_obj != mp_const_none) {
|
||||
x = ndarray_from_mp_obj(args[1].u_obj, 0); // x must hold an increasing sequence of independent values
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(x->dtype)
|
||||
if((x->ndim != 1) || (y->len != x->len)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("trapz is defined for 1D arrays of equal length"));
|
||||
}
|
||||
|
||||
mp_float_t (*funcx)(void *) = ndarray_get_float_function(x->dtype);
|
||||
uint8_t *xarray = (uint8_t *)x->array;
|
||||
mp_float_t x1, x2;
|
||||
|
||||
y1 = funcy(yarray);
|
||||
yarray += y->strides[ULAB_MAX_DIMS - 1];
|
||||
x1 = funcx(xarray);
|
||||
xarray += x->strides[ULAB_MAX_DIMS - 1];
|
||||
|
||||
for(size_t i=1; i < y->len; i++) {
|
||||
y2 = funcy(yarray);
|
||||
yarray += y->strides[ULAB_MAX_DIMS - 1];
|
||||
x2 = funcx(xarray);
|
||||
xarray += x->strides[ULAB_MAX_DIMS - 1];
|
||||
mp_float_t value = (x2 - x1) * (y2 + y1);
|
||||
m = mean + (value - mean) / (mp_float_t)count;
|
||||
mean = m;
|
||||
x1 = x2;
|
||||
y1 = y2;
|
||||
count++;
|
||||
}
|
||||
} else {
|
||||
mp_float_t dx = mp_obj_get_float(args[2].u_obj);
|
||||
y1 = funcy(yarray);
|
||||
yarray += y->strides[ULAB_MAX_DIMS - 1];
|
||||
|
||||
for(size_t i=1; i < y->len; i++) {
|
||||
y2 = ndarray_get_float_index(y->array, y->dtype, i);
|
||||
mp_float_t value = (y2 + y1);
|
||||
m = mean + (value - mean) / (mp_float_t)count;
|
||||
mean = m;
|
||||
y1 = y2;
|
||||
count++;
|
||||
}
|
||||
mean *= dx;
|
||||
}
|
||||
return mp_obj_new_float(MICROPY_FLOAT_CONST(0.5)*mean*(y->len-1));
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(approx_trapz_obj, 1, approx_trapz);
|
||||
#endif
|
||||
29
code/numpy/approx.h
Normal file
29
code/numpy/approx.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2021 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _APPROX_
|
||||
#define _APPROX_
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "../ndarray.h"
|
||||
|
||||
#define APPROX_EPS MICROPY_FLOAT_CONST(1.0e-4)
|
||||
#define APPROX_NONZDELTA MICROPY_FLOAT_CONST(0.05)
|
||||
#define APPROX_ZDELTA MICROPY_FLOAT_CONST(0.00025)
|
||||
#define APPROX_ALPHA MICROPY_FLOAT_CONST(1.0)
|
||||
#define APPROX_BETA MICROPY_FLOAT_CONST(2.0)
|
||||
#define APPROX_GAMMA MICROPY_FLOAT_CONST(0.5)
|
||||
#define APPROX_DELTA MICROPY_FLOAT_CONST(0.5)
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(approx_interp_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(approx_trapz_obj);
|
||||
|
||||
#endif /* _APPROX_ */
|
||||
431
code/numpy/bitwise.c
Normal file
431
code/numpy/bitwise.c
Normal file
|
|
@ -0,0 +1,431 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2023 Zoltán Vörös
|
||||
*
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
|
||||
#include "bitwise.h"
|
||||
|
||||
|
||||
#if ULAB_NUMPY_HAS_BITWISE_AND
|
||||
ndarray_obj_t *bitwise_bitwise_and_loop(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
|
||||
// AND is commutative, so simply swap the order, if a particular combination has already been inspected
|
||||
|
||||
ndarray_obj_t *results = NULL;
|
||||
uint8_t *larray = (uint8_t *)lhs->array;
|
||||
uint8_t *rarray = (uint8_t *)rhs->array;
|
||||
|
||||
if(lhs->dtype == NDARRAY_UINT8) {
|
||||
if(rhs->dtype == NDARRAY_UINT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8);
|
||||
BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, &);
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, &);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
|
||||
BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, &);
|
||||
} else if(rhs->dtype == NDARRAY_INT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, &);
|
||||
}
|
||||
} else if(lhs->dtype == NDARRAY_INT8) {
|
||||
if(rhs->dtype == NDARRAY_INT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8);
|
||||
BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, &);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
|
||||
BINARY_LOOP(results, uint16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, &);
|
||||
} else if(rhs->dtype == NDARRAY_INT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, &);
|
||||
} else {
|
||||
return bitwise_bitwise_and_loop(rhs, lhs, ndim, shape, rstrides, lstrides);
|
||||
}
|
||||
} else if(lhs->dtype == NDARRAY_UINT16) {
|
||||
if(rhs->dtype == NDARRAY_UINT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
|
||||
BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, &);
|
||||
} else if(rhs->dtype == NDARRAY_INT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, &);
|
||||
} else {
|
||||
return bitwise_bitwise_and_loop(rhs, lhs, ndim, shape, rstrides, lstrides);
|
||||
}
|
||||
} else if(lhs->dtype == NDARRAY_INT16) {
|
||||
if(rhs->dtype == NDARRAY_INT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, &);
|
||||
} else {
|
||||
return bitwise_bitwise_and_loop(rhs, lhs, ndim, shape, rstrides, lstrides);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_BITWISE_OR
|
||||
ndarray_obj_t *bitwise_bitwise_or_loop(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
|
||||
// OR is commutative, so simply swap the order, if a particular combination has already been inspected
|
||||
|
||||
ndarray_obj_t *results = NULL;
|
||||
uint8_t *larray = (uint8_t *)lhs->array;
|
||||
uint8_t *rarray = (uint8_t *)rhs->array;
|
||||
|
||||
if(lhs->dtype == NDARRAY_UINT8) {
|
||||
if(rhs->dtype == NDARRAY_UINT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8);
|
||||
BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, |);
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, |);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
|
||||
BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, |);
|
||||
} else if(rhs->dtype == NDARRAY_INT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, |);
|
||||
}
|
||||
} else if(lhs->dtype == NDARRAY_INT8) {
|
||||
if(rhs->dtype == NDARRAY_INT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8);
|
||||
BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, |);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
|
||||
BINARY_LOOP(results, uint16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, |);
|
||||
} else if(rhs->dtype == NDARRAY_INT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, |);
|
||||
} else {
|
||||
return bitwise_bitwise_or_loop(rhs, lhs, ndim, shape, rstrides, lstrides);
|
||||
}
|
||||
} else if(lhs->dtype == NDARRAY_UINT16) {
|
||||
if(rhs->dtype == NDARRAY_UINT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
|
||||
BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, |);
|
||||
} else if(rhs->dtype == NDARRAY_INT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, |);
|
||||
} else {
|
||||
return bitwise_bitwise_or_loop(rhs, lhs, ndim, shape, rstrides, lstrides);
|
||||
}
|
||||
} else if(lhs->dtype == NDARRAY_INT16) {
|
||||
if(rhs->dtype == NDARRAY_INT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, |);
|
||||
} else {
|
||||
return bitwise_bitwise_or_loop(rhs, lhs, ndim, shape, rstrides, lstrides);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if ULAB_NUMPY_HAS_BITWISE_XOR
|
||||
ndarray_obj_t *bitwise_bitwise_xor_loop(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
|
||||
// OR is commutative, so simply swap the order, if a particular combination has already been inspected
|
||||
|
||||
ndarray_obj_t *results = NULL;
|
||||
uint8_t *larray = (uint8_t *)lhs->array;
|
||||
uint8_t *rarray = (uint8_t *)rhs->array;
|
||||
|
||||
if(lhs->dtype == NDARRAY_UINT8) {
|
||||
if(rhs->dtype == NDARRAY_UINT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8);
|
||||
BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, ^);
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, ^);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
|
||||
BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, ^);
|
||||
} else if(rhs->dtype == NDARRAY_INT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, ^);
|
||||
}
|
||||
} else if(lhs->dtype == NDARRAY_INT8) {
|
||||
if(rhs->dtype == NDARRAY_INT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8);
|
||||
BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, ^);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
|
||||
BINARY_LOOP(results, uint16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, ^);
|
||||
} else if(rhs->dtype == NDARRAY_INT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, ^);
|
||||
} else {
|
||||
return bitwise_bitwise_xor_loop(rhs, lhs, ndim, shape, rstrides, lstrides);
|
||||
}
|
||||
} else if(lhs->dtype == NDARRAY_UINT16) {
|
||||
if(rhs->dtype == NDARRAY_UINT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
|
||||
BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, ^);
|
||||
} else if(rhs->dtype == NDARRAY_INT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, ^);
|
||||
} else {
|
||||
return bitwise_bitwise_xor_loop(rhs, lhs, ndim, shape, rstrides, lstrides);
|
||||
}
|
||||
} else if(lhs->dtype == NDARRAY_INT16) {
|
||||
if(rhs->dtype == NDARRAY_INT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, ^);
|
||||
} else {
|
||||
return bitwise_bitwise_xor_loop(rhs, lhs, ndim, shape, rstrides, lstrides);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_LEFT_SHIFT
|
||||
ndarray_obj_t *bitwise_left_shift_loop(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
|
||||
ndarray_obj_t *results = NULL;
|
||||
uint8_t *larray = (uint8_t *)lhs->array;
|
||||
uint8_t *rarray = (uint8_t *)rhs->array;
|
||||
|
||||
if(lhs->dtype == NDARRAY_UINT8) {
|
||||
if(rhs->dtype == NDARRAY_UINT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8);
|
||||
BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, <<);
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, <<);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
|
||||
BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, <<);
|
||||
} else {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, <<);
|
||||
}
|
||||
} else if(lhs->dtype == NDARRAY_INT8) {
|
||||
if(rhs->dtype == NDARRAY_UINT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, int8_t, uint8_t, larray, lstrides, rarray, rstrides, <<);
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8);
|
||||
BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, <<);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
|
||||
BINARY_LOOP(results, uint16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, <<);
|
||||
} else {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, <<);
|
||||
}
|
||||
} else if(lhs->dtype == NDARRAY_UINT16) {
|
||||
if(rhs->dtype == NDARRAY_UINT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
|
||||
BINARY_LOOP(results, uint16_t, uint16_t, uint8_t, larray, lstrides, rarray, rstrides, <<);
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8);
|
||||
BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, <<);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
|
||||
BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, <<);
|
||||
} else {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, <<);
|
||||
}
|
||||
} else if(lhs->dtype == NDARRAY_INT16) {
|
||||
if(rhs->dtype == NDARRAY_UINT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, int16_t, uint8_t, larray, lstrides, rarray, rstrides, <<);
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, int16_t, int8_t, larray, lstrides, rarray, rstrides, <<);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, uint16_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, <<);
|
||||
} else {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, <<);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_RIGHT_SHIFT
|
||||
ndarray_obj_t *bitwise_right_shift_loop(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
|
||||
ndarray_obj_t *results = NULL;
|
||||
uint8_t *larray = (uint8_t *)lhs->array;
|
||||
uint8_t *rarray = (uint8_t *)rhs->array;
|
||||
|
||||
if(lhs->dtype == NDARRAY_UINT8) {
|
||||
if(rhs->dtype == NDARRAY_UINT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8);
|
||||
BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, >>);
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, >>);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
|
||||
BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, >>);
|
||||
} else {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, >>);
|
||||
}
|
||||
} else if(lhs->dtype == NDARRAY_INT8) {
|
||||
if(rhs->dtype == NDARRAY_UINT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, int8_t, uint8_t, larray, lstrides, rarray, rstrides, >>);
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8);
|
||||
BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, >>);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
|
||||
BINARY_LOOP(results, uint16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, >>);
|
||||
} else {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, >>);
|
||||
}
|
||||
} else if(lhs->dtype == NDARRAY_UINT16) {
|
||||
if(rhs->dtype == NDARRAY_UINT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
|
||||
BINARY_LOOP(results, uint16_t, uint16_t, uint8_t, larray, lstrides, rarray, rstrides, >>);
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8);
|
||||
BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, >>);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16);
|
||||
BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, >>);
|
||||
} else {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, >>);
|
||||
}
|
||||
} else if(lhs->dtype == NDARRAY_INT16) {
|
||||
if(rhs->dtype == NDARRAY_UINT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, int16_t, uint8_t, larray, lstrides, rarray, rstrides, >>);
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, int16_t, int8_t, larray, lstrides, rarray, rstrides, >>);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, uint16_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, >>);
|
||||
} else {
|
||||
results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16);
|
||||
BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, >>);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
#endif
|
||||
|
||||
mp_obj_t *bitwise_binary_operators(mp_obj_t x1, mp_obj_t x2, uint8_t optype) {
|
||||
ndarray_obj_t *lhs = ndarray_from_mp_obj(x1, 0);
|
||||
ndarray_obj_t *rhs = ndarray_from_mp_obj(x2, 0);
|
||||
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
if((lhs->dtype == NDARRAY_FLOAT) || (rhs->dtype == NDARRAY_FLOAT) || (lhs->dtype == NDARRAY_COMPLEX) || (rhs->dtype == NDARRAY_COMPLEX)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("not supported for input types"));
|
||||
}
|
||||
#else
|
||||
if((lhs->dtype == NDARRAY_FLOAT) || (rhs->dtype == NDARRAY_FLOAT)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("not supported for input types"));
|
||||
}
|
||||
#endif
|
||||
|
||||
uint8_t ndim = 0;
|
||||
size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
|
||||
int32_t *lstrides = m_new0(int32_t, ULAB_MAX_DIMS);
|
||||
int32_t *rstrides = m_new0(int32_t, ULAB_MAX_DIMS);
|
||||
|
||||
if(!ndarray_can_broadcast(lhs, rhs, &ndim, shape, lstrides, rstrides)) {
|
||||
m_del(size_t, shape, ULAB_MAX_DIMS);
|
||||
m_del(int32_t, lstrides, ULAB_MAX_DIMS);
|
||||
m_del(int32_t, rstrides, ULAB_MAX_DIMS);
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("operands could not be broadcast together"));
|
||||
}
|
||||
|
||||
ndarray_obj_t *results = NULL;
|
||||
|
||||
switch(optype) {
|
||||
#if ULAB_NUMPY_HAS_BITWISE_AND
|
||||
case BITWISE_AND:
|
||||
results = bitwise_bitwise_and_loop(lhs, rhs, ndim, shape, lstrides, rstrides);
|
||||
break;
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_BITWISE_OR
|
||||
case BITWISE_OR:
|
||||
results = bitwise_bitwise_or_loop(lhs, rhs, ndim, shape, lstrides, rstrides);
|
||||
break;
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_BITWISE_XOR
|
||||
case BITWISE_XOR:
|
||||
results = bitwise_bitwise_xor_loop(lhs, rhs, ndim, shape, lstrides, rstrides);
|
||||
break;
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_LEFT_SHIFT
|
||||
case BITWISE_LEFT_SHIFT:
|
||||
results = bitwise_left_shift_loop(lhs, rhs, ndim, shape, lstrides, rstrides);
|
||||
break;
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_RIGHT_SHIFT
|
||||
case BITWISE_RIGHT_SHIFT:
|
||||
results = bitwise_right_shift_loop(lhs, rhs, ndim, shape, lstrides, rstrides);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
m_del(size_t, shape, ULAB_MAX_DIMS);
|
||||
m_del(int32_t, lstrides, ULAB_MAX_DIMS);
|
||||
m_del(int32_t, rstrides, ULAB_MAX_DIMS);
|
||||
|
||||
return MP_OBJ_FROM_PTR(results);
|
||||
}
|
||||
|
||||
#if ULAB_NUMPY_HAS_BITWISE_AND
|
||||
mp_obj_t bitwise_bitwise_and(mp_obj_t x1, mp_obj_t x2) {
|
||||
return bitwise_binary_operators(x1, x2, BITWISE_AND);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(bitwise_bitwise_and_obj, bitwise_bitwise_and);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_BITWISE_OR
|
||||
mp_obj_t bitwise_bitwise_or(mp_obj_t x1, mp_obj_t x2) {
|
||||
return bitwise_binary_operators(x1, x2, BITWISE_OR);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(bitwise_bitwise_or_obj, bitwise_bitwise_or);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_BITWISE_XOR
|
||||
mp_obj_t bitwise_bitwise_xor(mp_obj_t x1, mp_obj_t x2) {
|
||||
return bitwise_binary_operators(x1, x2, BITWISE_XOR);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(bitwise_bitwise_xor_obj, bitwise_bitwise_xor);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_LEFT_SHIFT
|
||||
mp_obj_t bitwise_left_shift(mp_obj_t x1, mp_obj_t x2) {
|
||||
return bitwise_binary_operators(x1, x2, BITWISE_LEFT_SHIFT);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(left_shift_obj, bitwise_left_shift);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_RIGHT_SHIFT
|
||||
mp_obj_t bitwise_right_shift(mp_obj_t x1, mp_obj_t x2) {
|
||||
return bitwise_binary_operators(x1, x2, BITWISE_RIGHT_SHIFT);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(right_shift_obj, bitwise_right_shift);
|
||||
#endif
|
||||
32
code/numpy/bitwise.h
Normal file
32
code/numpy/bitwise.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2023 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _BITWISE_
|
||||
#define _BITWISE_
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "../ndarray.h"
|
||||
|
||||
enum BITWISE_FUNCTION_TYPE {
|
||||
BITWISE_AND,
|
||||
BITWISE_OR,
|
||||
BITWISE_XOR,
|
||||
BITWISE_LEFT_SHIFT,
|
||||
BITWISE_RIGHT_SHIFT,
|
||||
};
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(bitwise_bitwise_and_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(bitwise_bitwise_or_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(bitwise_bitwise_xor_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(left_shift_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(right_shift_obj);
|
||||
|
||||
#endif /* _BITWISE_ */
|
||||
834
code/numpy/carray/carray.c
Normal file
834
code/numpy/carray/carray.c
Normal file
|
|
@ -0,0 +1,834 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021-2022 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/objint.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/builtin.h"
|
||||
#include "py/misc.h"
|
||||
|
||||
#include "../../ulab.h"
|
||||
#include "../../ndarray.h"
|
||||
#include "../../ulab_tools.h"
|
||||
#include "carray.h"
|
||||
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
|
||||
//| import builtins
|
||||
//|
|
||||
//| import ulab.numpy
|
||||
|
||||
//| def real(val: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
|
||||
//| """
|
||||
//| Return the real part of the complex argument, which can be
|
||||
//| either an ndarray, or a scalar."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
mp_obj_t carray_real(mp_obj_t _source) {
|
||||
if(mp_obj_is_type(_source, &ulab_ndarray_type)) {
|
||||
ndarray_obj_t *source = MP_OBJ_TO_PTR(_source);
|
||||
if(source->dtype != NDARRAY_COMPLEX) {
|
||||
ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, source->dtype);
|
||||
ndarray_copy_array(source, target, 0);
|
||||
return MP_OBJ_FROM_PTR(target);
|
||||
} else { // the input is most definitely a complex array
|
||||
ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT);
|
||||
ndarray_copy_array(source, target, 0);
|
||||
return MP_OBJ_FROM_PTR(target);
|
||||
}
|
||||
} else {
|
||||
mp_raise_NotImplementedError(MP_ERROR_TEXT("function is implemented for ndarrays only"));
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(carray_real_obj, carray_real);
|
||||
|
||||
//| def imag(val: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
|
||||
//| """
|
||||
//| Return the imaginary part of the complex argument, which can be
|
||||
//| either an ndarray, or a scalar."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
mp_obj_t carray_imag(mp_obj_t _source) {
|
||||
if(mp_obj_is_type(_source, &ulab_ndarray_type)) {
|
||||
ndarray_obj_t *source = MP_OBJ_TO_PTR(_source);
|
||||
if(source->dtype != NDARRAY_COMPLEX) { // if not complex, then the imaginary part is zero
|
||||
ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, source->dtype);
|
||||
return MP_OBJ_FROM_PTR(target);
|
||||
} else { // the input is most definitely a complex array
|
||||
ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT);
|
||||
ndarray_copy_array(source, target, source->itemsize / 2);
|
||||
return MP_OBJ_FROM_PTR(target);
|
||||
}
|
||||
} else {
|
||||
mp_raise_NotImplementedError(MP_ERROR_TEXT("function is implemented for ndarrays only"));
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(carray_imag_obj, carray_imag);
|
||||
|
||||
#if ULAB_NUMPY_HAS_CONJUGATE
|
||||
|
||||
//| def conjugate(
|
||||
//| val: builtins.complex | ulab.numpy.ndarray
|
||||
//| ) -> builtins.complex | ulab.numpy.ndarray:
|
||||
//| """
|
||||
//| Return the conjugate of the complex argument, which can be
|
||||
//| either an ndarray, or a scalar."""
|
||||
//| ...
|
||||
//|
|
||||
mp_obj_t carray_conjugate(mp_obj_t _source) {
|
||||
if(mp_obj_is_type(_source, &ulab_ndarray_type)) {
|
||||
ndarray_obj_t *source = MP_OBJ_TO_PTR(_source);
|
||||
ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, source->dtype);
|
||||
ndarray_copy_array(source, ndarray, 0);
|
||||
if(source->dtype == NDARRAY_COMPLEX) {
|
||||
mp_float_t *array = (mp_float_t *)ndarray->array;
|
||||
array++;
|
||||
for(size_t i = 0; i < ndarray->len; i++) {
|
||||
*array *= MICROPY_FLOAT_CONST(-1.0);
|
||||
array += 2;
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
} else {
|
||||
if(mp_obj_is_type(_source, &mp_type_complex)) {
|
||||
mp_float_t real, imag;
|
||||
mp_obj_get_complex(_source, &real, &imag);
|
||||
imag = imag * MICROPY_FLOAT_CONST(-1.0);
|
||||
return mp_obj_new_complex(real, imag);
|
||||
} else if(mp_obj_is_int(_source) || mp_obj_is_float(_source)) {
|
||||
return _source;
|
||||
} else {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("input must be an ndarray, or a scalar"));
|
||||
}
|
||||
}
|
||||
// this should never happen
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(carray_conjugate_obj, carray_conjugate);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_SORT_COMPLEX
|
||||
//| def sort_complex(a: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
|
||||
//| """
|
||||
//| .. param: a
|
||||
//| a one-dimensional ndarray
|
||||
//|
|
||||
//| Sort a complex array using the real part first, then the imaginary part.
|
||||
//| Always returns a sorted complex array, even if the input was real."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static void carray_sort_complex_(mp_float_t *array, size_t len) {
|
||||
// array is assumed to be a floating vector containing the real and imaginary parts
|
||||
// of a complex array at alternating positions as
|
||||
// array[0] = real[0]
|
||||
// array[1] = imag[0]
|
||||
// array[2] = real[1]
|
||||
// array[3] = imag[1]
|
||||
|
||||
mp_float_t real, imag;
|
||||
size_t c, q = len, p, r = len >> 1;
|
||||
for (;;) {
|
||||
if (r > 0) {
|
||||
r--;
|
||||
real = array[2 * r];
|
||||
imag = array[2 * r + 1];
|
||||
} else {
|
||||
q--;
|
||||
if(q == 0) {
|
||||
break;
|
||||
}
|
||||
real = array[2 * q];
|
||||
imag = array[2 * q + 1];
|
||||
array[2 * q] = array[0];
|
||||
array[2 * q + 1] = array[1];
|
||||
}
|
||||
p = r;
|
||||
c = r + r + 1;
|
||||
while (c < q) {
|
||||
if(c + 1 < q) {
|
||||
if((array[2 * (c+1)] > array[2 * c]) ||
|
||||
((array[2 * (c+1)] == array[2 * c]) && (array[2 * (c+1) + 1] > array[2 * c + 1]))) {
|
||||
c++;
|
||||
}
|
||||
}
|
||||
if((array[2 * c] > real) ||
|
||||
((array[2 * c] == real) && (array[2 * c + 1] > imag))) {
|
||||
array[2 * p] = array[2 * c]; // real part
|
||||
array[2 * p + 1] = array[2 * c + 1]; // imag part
|
||||
p = c;
|
||||
c = p + p + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
array[2 * p] = real;
|
||||
array[2 * p + 1] = imag;
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_t carray_sort_complex(mp_obj_t _source) {
|
||||
if(!mp_obj_is_type(_source, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("input must be a 1D ndarray"));
|
||||
}
|
||||
ndarray_obj_t *source = MP_OBJ_TO_PTR(_source);
|
||||
if(source->ndim != 1) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("input must be a 1D ndarray"));
|
||||
}
|
||||
|
||||
ndarray_obj_t *ndarray = ndarray_copy_view_convert_type(source, NDARRAY_COMPLEX);
|
||||
|
||||
if(ndarray->len != 0) {
|
||||
mp_float_t *array = (mp_float_t *)ndarray->array;
|
||||
carray_sort_complex_(array, ndarray->len);
|
||||
}
|
||||
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(carray_sort_complex_obj, carray_sort_complex);
|
||||
#endif
|
||||
|
||||
//| def abs(a: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
|
||||
//| """
|
||||
//| .. param: a
|
||||
//| a one-dimensional ndarray
|
||||
//|
|
||||
//| Return the absolute value of complex ndarray."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
mp_obj_t carray_abs(ndarray_obj_t *source, ndarray_obj_t *target) {
|
||||
// calculates the absolute value of a complex array and returns a dense array
|
||||
uint8_t *sarray = (uint8_t *)source->array;
|
||||
mp_float_t *tarray = (mp_float_t *)target->array;
|
||||
uint8_t itemsize = mp_binary_get_size('@', NDARRAY_FLOAT, NULL);
|
||||
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t i = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
size_t j = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
do {
|
||||
mp_float_t rvalue = *(mp_float_t *)sarray;
|
||||
mp_float_t ivalue = *(mp_float_t *)(sarray + itemsize);
|
||||
*tarray++ = MICROPY_FLOAT_C_FUN(sqrt)(rvalue * rvalue + ivalue * ivalue);
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 1];
|
||||
l++;
|
||||
} while(l < source->shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < source->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 3];
|
||||
j++;
|
||||
} while(j < source->shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 4];
|
||||
i++;
|
||||
} while(i < source->shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif
|
||||
return MP_OBJ_FROM_PTR(target);
|
||||
}
|
||||
|
||||
static void carray_copy_part(uint8_t *tarray, uint8_t *sarray, size_t *shape, int32_t *strides) {
|
||||
// copies the real or imaginary part of an array
|
||||
// into the respective part of a dense complex array
|
||||
uint8_t sz = sizeof(mp_float_t);
|
||||
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t i = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
size_t j = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
do {
|
||||
memcpy(tarray, sarray, sz);
|
||||
tarray += 2 * sz;
|
||||
sarray += strides[ULAB_MAX_DIMS - 1];
|
||||
l++;
|
||||
} while(l < shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
sarray -= strides[ULAB_MAX_DIMS - 1] * shape[ULAB_MAX_DIMS-1];
|
||||
sarray += strides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif /* ULAB_MAX_DIMS > 1 */
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
sarray -= strides[ULAB_MAX_DIMS - 2] * shape[ULAB_MAX_DIMS-2];
|
||||
sarray += strides[ULAB_MAX_DIMS - 3];
|
||||
j++;
|
||||
} while(j < shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif /* ULAB_MAX_DIMS > 2 */
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
sarray -= strides[ULAB_MAX_DIMS - 3] * shape[ULAB_MAX_DIMS-3];
|
||||
sarray += strides[ULAB_MAX_DIMS - 4];
|
||||
i++;
|
||||
} while(i < shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif /* ULAB_MAX_DIMS > 3 */
|
||||
}
|
||||
|
||||
mp_obj_t carray_binary_equal_not_equal(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
|
||||
uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides, mp_binary_op_t op) {
|
||||
|
||||
ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8);
|
||||
results->boolean = 1;
|
||||
uint8_t *array = (uint8_t *)results->array;
|
||||
|
||||
if(op == MP_BINARY_OP_NOT_EQUAL) {
|
||||
memset(array, 1, results->len);
|
||||
}
|
||||
|
||||
if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) {
|
||||
mp_float_t *larray = (mp_float_t *)lhs->array;
|
||||
mp_float_t *rarray = (mp_float_t *)rhs->array;
|
||||
|
||||
ulab_rescale_float_strides(lstrides);
|
||||
ulab_rescale_float_strides(rstrides);
|
||||
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t i = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
size_t j = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
do {
|
||||
if((larray[0] == rarray[0]) && (larray[1] == rarray[1])) {
|
||||
*array ^= 0x01;
|
||||
}
|
||||
array++;
|
||||
larray += lstrides[ULAB_MAX_DIMS - 1];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 1];
|
||||
l++;
|
||||
} while(l < results->shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
larray -= lstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
|
||||
larray += lstrides[ULAB_MAX_DIMS - 2];
|
||||
rarray -= rstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < results->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif /* ULAB_MAX_DIMS > 1 */
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
larray -= lstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
|
||||
larray += lstrides[ULAB_MAX_DIMS - 3];
|
||||
rarray -= rstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 3];
|
||||
j++;
|
||||
} while(j < results->shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif /* ULAB_MAX_DIMS > 2 */
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
larray -= lstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
|
||||
larray += lstrides[ULAB_MAX_DIMS - 4];
|
||||
rarray -= rstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 4];
|
||||
i++;
|
||||
} while(i < results->shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif /* ULAB_MAX_DIMS > 3 */
|
||||
} else { // only one of the operands is complex
|
||||
mp_float_t *larray = (mp_float_t *)lhs->array;
|
||||
uint8_t *rarray = (uint8_t *)rhs->array;
|
||||
|
||||
// align the complex array to the left
|
||||
uint8_t rdtype = rhs->dtype;
|
||||
int32_t *lstrides_ = lstrides;
|
||||
int32_t *rstrides_ = rstrides;
|
||||
|
||||
if(rhs->dtype == NDARRAY_COMPLEX) {
|
||||
larray = (mp_float_t *)rhs->array;
|
||||
rarray = (uint8_t *)lhs->array;
|
||||
lstrides_ = rstrides;
|
||||
rstrides_ = lstrides;
|
||||
rdtype = lhs->dtype;
|
||||
}
|
||||
|
||||
ulab_rescale_float_strides(lstrides_);
|
||||
|
||||
if(rdtype == NDARRAY_UINT8) {
|
||||
BINARY_LOOP_COMPLEX_EQUAL(results, array, uint8_t, larray, lstrides_, rarray, rstrides_);
|
||||
} else if(rdtype == NDARRAY_INT8) {
|
||||
BINARY_LOOP_COMPLEX_EQUAL(results, array, int8_t, larray, lstrides_, rarray, rstrides_);
|
||||
} else if(rdtype == NDARRAY_UINT16) {
|
||||
BINARY_LOOP_COMPLEX_EQUAL(results, array, uint16_t, larray, lstrides_, rarray, rstrides_);
|
||||
} else if(rdtype == NDARRAY_INT16) {
|
||||
BINARY_LOOP_COMPLEX_EQUAL(results, array, int16_t, larray, lstrides_, rarray, rstrides_);
|
||||
} else if(rdtype == NDARRAY_FLOAT) {
|
||||
BINARY_LOOP_COMPLEX_EQUAL(results, array, mp_float_t, larray, lstrides_, rarray, rstrides_);
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(results);
|
||||
}
|
||||
|
||||
mp_obj_t carray_binary_add(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
|
||||
uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
|
||||
|
||||
ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_COMPLEX);
|
||||
mp_float_t *resarray = (mp_float_t *)results->array;
|
||||
|
||||
if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) {
|
||||
mp_float_t *larray = (mp_float_t *)lhs->array;
|
||||
mp_float_t *rarray = (mp_float_t *)rhs->array;
|
||||
|
||||
ulab_rescale_float_strides(lstrides);
|
||||
ulab_rescale_float_strides(rstrides);
|
||||
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t i = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
size_t j = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
do {
|
||||
// real part
|
||||
*resarray++ = larray[0] + rarray[0];
|
||||
// imaginary part
|
||||
*resarray++ = larray[1] + rarray[1];
|
||||
larray += lstrides[ULAB_MAX_DIMS - 1];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 1];
|
||||
l++;
|
||||
} while(l < results->shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
larray -= lstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
|
||||
larray += lstrides[ULAB_MAX_DIMS - 2];
|
||||
rarray -= rstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < results->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif /* ULAB_MAX_DIMS > 1 */
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
larray -= lstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
|
||||
larray += lstrides[ULAB_MAX_DIMS - 3];
|
||||
rarray -= rstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 3];
|
||||
j++;
|
||||
} while(j < results->shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif /* ULAB_MAX_DIMS > 2 */
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
larray -= lstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
|
||||
larray += lstrides[ULAB_MAX_DIMS - 4];
|
||||
rarray -= rstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 4];
|
||||
i++;
|
||||
} while(i < results->shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif /* ULAB_MAX_DIMS > 3 */
|
||||
} else { // only one of the operands is complex
|
||||
uint8_t *larray = (uint8_t *)lhs->array;
|
||||
uint8_t *rarray = (uint8_t *)rhs->array;
|
||||
|
||||
// align the complex array to the left
|
||||
uint8_t rdtype = rhs->dtype;
|
||||
int32_t *lstrides_ = lstrides;
|
||||
int32_t *rstrides_ = rstrides;
|
||||
|
||||
if(rhs->dtype == NDARRAY_COMPLEX) {
|
||||
larray = (uint8_t *)rhs->array;
|
||||
rarray = (uint8_t *)lhs->array;
|
||||
lstrides_ = rstrides;
|
||||
rstrides_ = lstrides;
|
||||
rdtype = lhs->dtype;
|
||||
}
|
||||
|
||||
if(rdtype == NDARRAY_UINT8) {
|
||||
BINARY_LOOP_COMPLEX(results, resarray, uint8_t, larray, lstrides_, rarray, rstrides_, +);
|
||||
} else if(rdtype == NDARRAY_INT8) {
|
||||
BINARY_LOOP_COMPLEX(results, resarray, int8_t, larray, lstrides_, rarray, rstrides_, +);
|
||||
} else if(rdtype == NDARRAY_UINT16) {
|
||||
BINARY_LOOP_COMPLEX(results, resarray, uint16_t, larray, lstrides_, rarray, rstrides_, +);
|
||||
} else if(rdtype == NDARRAY_INT16) {
|
||||
BINARY_LOOP_COMPLEX(results, resarray, int16_t, larray, lstrides_, rarray, rstrides_, +);
|
||||
} else if(rdtype == NDARRAY_FLOAT) {
|
||||
BINARY_LOOP_COMPLEX(results, resarray, mp_float_t, larray, lstrides_, rarray, rstrides_, +);
|
||||
}
|
||||
|
||||
// simply copy the imaginary part
|
||||
uint8_t *tarray = (uint8_t *)results->array;
|
||||
tarray += sizeof(mp_float_t);
|
||||
|
||||
if(lhs->dtype == NDARRAY_COMPLEX) {
|
||||
rarray = (uint8_t *)lhs->array;
|
||||
rstrides = lstrides;
|
||||
} else {
|
||||
rarray = (uint8_t *)rhs->array;
|
||||
}
|
||||
rarray += sizeof(mp_float_t);
|
||||
carray_copy_part(tarray, rarray, results->shape, rstrides);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(results);
|
||||
}
|
||||
|
||||
static void carray_binary_multiply_(ndarray_obj_t *results, mp_float_t *resarray, uint8_t *larray, uint8_t *rarray,
|
||||
int32_t *lstrides, int32_t *rstrides, uint8_t rdtype) {
|
||||
|
||||
if(rdtype == NDARRAY_UINT8) {
|
||||
BINARY_LOOP_COMPLEX(results, resarray, uint8_t, larray, lstrides, rarray, rstrides, *);
|
||||
} else if(rdtype == NDARRAY_INT8) {
|
||||
BINARY_LOOP_COMPLEX(results, resarray, int8_t, larray, lstrides, rarray, rstrides, *);
|
||||
} else if(rdtype == NDARRAY_UINT16) {
|
||||
BINARY_LOOP_COMPLEX(results, resarray, uint16_t, larray, lstrides, rarray, rstrides, *);
|
||||
} else if(rdtype == NDARRAY_INT16) {
|
||||
BINARY_LOOP_COMPLEX(results, resarray, int16_t, larray, lstrides, rarray, rstrides, *);
|
||||
} else if(rdtype == NDARRAY_FLOAT) {
|
||||
BINARY_LOOP_COMPLEX(results, resarray, mp_float_t, larray, lstrides, rarray, rstrides, *);
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_t carray_binary_multiply(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
|
||||
uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
|
||||
|
||||
ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_COMPLEX);
|
||||
mp_float_t *resarray = (mp_float_t *)results->array;
|
||||
|
||||
if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) {
|
||||
mp_float_t *larray = (mp_float_t *)lhs->array;
|
||||
mp_float_t *rarray = (mp_float_t *)rhs->array;
|
||||
|
||||
ulab_rescale_float_strides(lstrides);
|
||||
ulab_rescale_float_strides(rstrides);
|
||||
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t i = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
size_t j = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
do {
|
||||
// real part
|
||||
*resarray++ = larray[0] * rarray[0] - larray[1] * rarray[1];
|
||||
// imaginary part
|
||||
*resarray++ = larray[0] * rarray[1] + larray[1] * rarray[0];
|
||||
larray += lstrides[ULAB_MAX_DIMS - 1];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 1];
|
||||
l++;
|
||||
} while(l < results->shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
larray -= lstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
|
||||
larray += lstrides[ULAB_MAX_DIMS - 2];
|
||||
rarray -= rstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < results->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif /* ULAB_MAX_DIMS > 1 */
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
larray -= lstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
|
||||
larray += lstrides[ULAB_MAX_DIMS - 3];
|
||||
rarray -= rstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 3];
|
||||
j++;
|
||||
} while(j < results->shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif /* ULAB_MAX_DIMS > 2 */
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
larray -= lstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
|
||||
larray += lstrides[ULAB_MAX_DIMS - 4];
|
||||
rarray -= rstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 4];
|
||||
i++;
|
||||
} while(i < results->shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif /* ULAB_MAX_DIMS > 3 */
|
||||
} else { // only one of the operands is complex
|
||||
|
||||
uint8_t *larray = (uint8_t *)lhs->array;
|
||||
uint8_t *rarray = (uint8_t *)rhs->array;
|
||||
uint8_t *lo = larray, *ro = rarray;
|
||||
int32_t *left_strides = lstrides;
|
||||
int32_t *right_strides = rstrides;
|
||||
uint8_t rdtype = rhs->dtype;
|
||||
|
||||
// align the complex array to the left
|
||||
if(rhs->dtype == NDARRAY_COMPLEX) {
|
||||
lo = (uint8_t *)rhs->array;
|
||||
ro = (uint8_t *)lhs->array;
|
||||
rdtype = lhs->dtype;
|
||||
left_strides = rstrides;
|
||||
right_strides = lstrides;
|
||||
}
|
||||
|
||||
larray = lo;
|
||||
rarray = ro;
|
||||
// real part
|
||||
carray_binary_multiply_(results, resarray, larray, rarray, left_strides, right_strides, rdtype);
|
||||
|
||||
larray = lo + sizeof(mp_float_t);
|
||||
rarray = ro;
|
||||
resarray = (mp_float_t *)results->array;
|
||||
resarray++;
|
||||
// imaginary part
|
||||
carray_binary_multiply_(results, resarray, larray, rarray, left_strides, right_strides, rdtype);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(results);
|
||||
}
|
||||
|
||||
mp_obj_t carray_binary_subtract(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
|
||||
uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
|
||||
|
||||
ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_COMPLEX);
|
||||
mp_float_t *resarray = (mp_float_t *)results->array;
|
||||
|
||||
if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) {
|
||||
mp_float_t *larray = (mp_float_t *)lhs->array;
|
||||
mp_float_t *rarray = (mp_float_t *)rhs->array;
|
||||
|
||||
ulab_rescale_float_strides(lstrides);
|
||||
ulab_rescale_float_strides(rstrides);
|
||||
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t i = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
size_t j = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
do {
|
||||
// real part
|
||||
*resarray++ = larray[0] - rarray[0];
|
||||
// imaginary part
|
||||
*resarray++ = larray[1] - rarray[1];
|
||||
larray += lstrides[ULAB_MAX_DIMS - 1];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 1];
|
||||
l++;
|
||||
} while(l < results->shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
larray -= lstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
|
||||
larray += lstrides[ULAB_MAX_DIMS - 2];
|
||||
rarray -= rstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < results->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif /* ULAB_MAX_DIMS > 1 */
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
larray -= lstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
|
||||
larray += lstrides[ULAB_MAX_DIMS - 3];
|
||||
rarray -= rstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 3];
|
||||
j++;
|
||||
} while(j < results->shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif /* ULAB_MAX_DIMS > 2 */
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
larray -= lstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
|
||||
larray += lstrides[ULAB_MAX_DIMS - 4];
|
||||
rarray -= rstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 4];
|
||||
i++;
|
||||
} while(i < results->shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif /* ULAB_MAX_DIMS > 3 */
|
||||
} else {
|
||||
uint8_t *larray = (uint8_t *)lhs->array;
|
||||
if(lhs->dtype == NDARRAY_COMPLEX) {
|
||||
uint8_t *rarray = (uint8_t *)rhs->array;
|
||||
if(rhs->dtype == NDARRAY_UINT8) {
|
||||
BINARY_LOOP_COMPLEX(results, resarray, uint8_t, larray, lstrides, rarray, rstrides, -);
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {
|
||||
BINARY_LOOP_COMPLEX(results, resarray, int8_t, larray, lstrides, rarray, rstrides, -);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
BINARY_LOOP_COMPLEX(results, resarray, uint16_t, larray, lstrides, rarray, rstrides, -);
|
||||
} else if(rhs->dtype == NDARRAY_INT16) {
|
||||
BINARY_LOOP_COMPLEX(results, resarray, int16_t, larray, lstrides, rarray, rstrides, -);
|
||||
} else if(rhs->dtype == NDARRAY_FLOAT) {
|
||||
BINARY_LOOP_COMPLEX(results, resarray, mp_float_t, larray, lstrides, rarray, rstrides, -);
|
||||
}
|
||||
// copy the imaginary part
|
||||
uint8_t *tarray = (uint8_t *)results->array;
|
||||
tarray += sizeof(mp_float_t);
|
||||
|
||||
larray = (uint8_t *)lhs->array;
|
||||
larray += sizeof(mp_float_t);
|
||||
|
||||
carray_copy_part(tarray, larray, results->shape, lstrides);
|
||||
} else if(rhs->dtype == NDARRAY_COMPLEX) {
|
||||
mp_float_t *rarray = (mp_float_t *)rhs->array;
|
||||
ulab_rescale_float_strides(rstrides);
|
||||
|
||||
if(lhs->dtype == NDARRAY_UINT8) {
|
||||
BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT(results, resarray, uint8_t, larray, lstrides, rarray, rstrides);
|
||||
} else if(lhs->dtype == NDARRAY_INT8) {
|
||||
BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT(results, resarray, int8_t, larray, lstrides, rarray, rstrides);
|
||||
} else if(lhs->dtype == NDARRAY_UINT16) {
|
||||
BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT(results, resarray, uint16_t, larray, lstrides, rarray, rstrides);
|
||||
} else if(lhs->dtype == NDARRAY_INT16) {
|
||||
BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT(results, resarray, int16_t, larray, lstrides, rarray, rstrides);
|
||||
} else if(lhs->dtype == NDARRAY_FLOAT) {
|
||||
BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT(results, resarray, mp_float_t, larray, lstrides, rarray, rstrides);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return MP_OBJ_FROM_PTR(results);
|
||||
}
|
||||
|
||||
static void carray_binary_left_divide_(ndarray_obj_t *results, mp_float_t *resarray, uint8_t *larray, uint8_t *rarray,
|
||||
int32_t *lstrides, int32_t *rstrides, uint8_t rdtype) {
|
||||
|
||||
if(rdtype == NDARRAY_UINT8) {
|
||||
BINARY_LOOP_COMPLEX(results, resarray, uint8_t, larray, lstrides, rarray, rstrides, /);
|
||||
} else if(rdtype == NDARRAY_INT8) {
|
||||
BINARY_LOOP_COMPLEX(results, resarray, int8_t, larray, lstrides, rarray, rstrides, /);
|
||||
} else if(rdtype == NDARRAY_UINT16) {
|
||||
BINARY_LOOP_COMPLEX(results, resarray, uint16_t, larray, lstrides, rarray, rstrides, /);
|
||||
} else if(rdtype == NDARRAY_INT16) {
|
||||
BINARY_LOOP_COMPLEX(results, resarray, int16_t, larray, lstrides, rarray, rstrides, /);
|
||||
} else if(rdtype == NDARRAY_FLOAT) {
|
||||
BINARY_LOOP_COMPLEX(results, resarray, mp_float_t, larray, lstrides, rarray, rstrides, /);
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_t carray_binary_divide(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
|
||||
uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
|
||||
|
||||
ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_COMPLEX);
|
||||
mp_float_t *resarray = (mp_float_t *)results->array;
|
||||
|
||||
if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) {
|
||||
mp_float_t *larray = (mp_float_t *)lhs->array;
|
||||
mp_float_t *rarray = (mp_float_t *)rhs->array;
|
||||
|
||||
ulab_rescale_float_strides(lstrides);
|
||||
ulab_rescale_float_strides(rstrides);
|
||||
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t i = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
size_t j = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
do {
|
||||
// (a + bi) / (c + di) =
|
||||
// (ac + bd) / (c^2 + d^2) + i (bc - ad) / (c^2 + d^2)
|
||||
// denominator
|
||||
mp_float_t denom = rarray[0] * rarray[0] + rarray[1] * rarray[1];
|
||||
|
||||
// real part
|
||||
*resarray++ = (larray[0] * rarray[0] + larray[1] * rarray[1]) / denom;
|
||||
// imaginary part
|
||||
*resarray++ = (larray[1] * rarray[0] - larray[0] * rarray[1]) / denom;
|
||||
larray += lstrides[ULAB_MAX_DIMS - 1];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 1];
|
||||
l++;
|
||||
} while(l < results->shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
larray -= lstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
|
||||
larray += lstrides[ULAB_MAX_DIMS - 2];
|
||||
rarray -= rstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < results->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif /* ULAB_MAX_DIMS > 1 */
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
larray -= lstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
|
||||
larray += lstrides[ULAB_MAX_DIMS - 3];
|
||||
rarray -= rstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 3];
|
||||
j++;
|
||||
} while(j < results->shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif /* ULAB_MAX_DIMS > 2 */
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
larray -= lstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
|
||||
larray += lstrides[ULAB_MAX_DIMS - 4];
|
||||
rarray -= rstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 4];
|
||||
i++;
|
||||
} while(i < results->shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif /* ULAB_MAX_DIMS > 3 */
|
||||
} else {
|
||||
uint8_t *larray = (uint8_t *)lhs->array;
|
||||
uint8_t *rarray = (uint8_t *)rhs->array;
|
||||
if(lhs->dtype == NDARRAY_COMPLEX) {
|
||||
// real part
|
||||
carray_binary_left_divide_(results, resarray, larray, rarray, lstrides, rstrides, rhs->dtype);
|
||||
// imaginary part
|
||||
resarray = (mp_float_t *)results->array;
|
||||
resarray++;
|
||||
larray = (uint8_t *)lhs->array;
|
||||
larray += sizeof(mp_float_t);
|
||||
rarray = (uint8_t *)rhs->array;
|
||||
carray_binary_left_divide_(results, resarray, larray, rarray, lstrides, rstrides, rhs->dtype);
|
||||
} else {
|
||||
if(lhs->dtype == NDARRAY_UINT8) {
|
||||
BINARY_LOOP_COMPLEX_RIGHT_DIVIDE(results, resarray, uint8_t, larray, lstrides, rarray, rstrides);
|
||||
} else if(lhs->dtype == NDARRAY_INT8) {
|
||||
BINARY_LOOP_COMPLEX_RIGHT_DIVIDE(results, resarray, int8_t, larray, lstrides, rarray, rstrides);
|
||||
} else if(lhs->dtype == NDARRAY_UINT16) {
|
||||
BINARY_LOOP_COMPLEX_RIGHT_DIVIDE(results, resarray, uint16_t, larray, lstrides, rarray, rstrides);
|
||||
} else if(lhs->dtype == NDARRAY_INT16) {
|
||||
BINARY_LOOP_COMPLEX_RIGHT_DIVIDE(results, resarray, int16_t, larray, lstrides, rarray, rstrides);
|
||||
} else if(lhs->dtype == NDARRAY_FLOAT) {
|
||||
BINARY_LOOP_COMPLEX_RIGHT_DIVIDE(results, resarray, mp_float_t, larray, lstrides, rarray, rstrides);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return MP_OBJ_FROM_PTR(results);
|
||||
}
|
||||
|
||||
#endif
|
||||
237
code/numpy/carray/carray.h
Normal file
237
code/numpy/carray/carray.h
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021-2022 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _CARRAY_
|
||||
#define _CARRAY_
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(carray_real_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(carray_imag_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(carray_conjugate_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(carray_sort_complex_obj);
|
||||
|
||||
|
||||
mp_obj_t carray_imag(mp_obj_t );
|
||||
mp_obj_t carray_real(mp_obj_t );
|
||||
|
||||
mp_obj_t carray_abs(ndarray_obj_t *, ndarray_obj_t *);
|
||||
mp_obj_t carray_binary_add(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
|
||||
mp_obj_t carray_binary_multiply(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
|
||||
mp_obj_t carray_binary_subtract(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
|
||||
mp_obj_t carray_binary_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
|
||||
mp_obj_t carray_binary_equal_not_equal(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *, mp_binary_op_t );
|
||||
|
||||
#define BINARY_LOOP_COMPLEX1(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*(resarray) = *((mp_float_t *)(larray)) OPERATOR *((type_right *)(rarray));\
|
||||
(resarray) += 2;\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
|
||||
#define BINARY_LOOP_COMPLEX2(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
BINARY_LOOP_COMPLEX1((results), (resarray), type_right, (larray), (lstrides), (rarray), (rstrides), OPERATOR);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
|
||||
#define BINARY_LOOP_COMPLEX3(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
BINARY_LOOP_COMPLEX2((results), (resarray), type_right, (larray), (lstrides), (rarray), (rstrides), OPERATOR);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
|
||||
#define BINARY_LOOP_COMPLEX4(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
|
||||
size_t i = 0;\
|
||||
do {\
|
||||
BINARY_LOOP_COMPLEX3((results), (resarray), type_right, (larray), (lstrides), (rarray), (rstrides), OPERATOR);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
|
||||
i++;\
|
||||
} while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
|
||||
|
||||
#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT1(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*(resarray)++ = *((type_left *)(larray)) - (rarray)[0];\
|
||||
*(resarray)++ = -(rarray)[1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
|
||||
#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT2(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT1((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
|
||||
#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT3(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT2((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
|
||||
#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT4(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
|
||||
size_t i = 0;\
|
||||
do {\
|
||||
BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT3((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
|
||||
i++;\
|
||||
} while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
|
||||
|
||||
#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE1(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
mp_float_t *c = (mp_float_t *)(rarray);\
|
||||
mp_float_t denom = c[0] * c[0] + c[1] * c[1];\
|
||||
mp_float_t a = *((type_left *)(larray)) / denom;\
|
||||
*(resarray)++ = a * c[0];\
|
||||
*(resarray)++ = -a * c[1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
|
||||
#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE2(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
BINARY_LOOP_COMPLEX_RIGHT_DIVIDE1((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
|
||||
#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE3(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
BINARY_LOOP_COMPLEX_RIGHT_DIVIDE2((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
|
||||
#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE4(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
|
||||
size_t i = 0;\
|
||||
do {\
|
||||
BINARY_LOOP_COMPLEX_RIGHT_DIVIDE3((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
|
||||
i++;\
|
||||
} while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
|
||||
|
||||
|
||||
#define BINARY_LOOP_COMPLEX_EQUAL1(results, array, type_right, larray, lstrides, rarray, rstrides)\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
if((*(larray) == *((type_right *)(rarray))) && ((larray)[1] == MICROPY_FLOAT_CONST(0.0))) {\
|
||||
*(array) ^= 0x01;\
|
||||
}\
|
||||
(array)++;\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
|
||||
#define BINARY_LOOP_COMPLEX_EQUAL2(results, array, type_right, larray, lstrides, rarray, rstrides)\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
BINARY_LOOP_COMPLEX_EQUAL1((results), (array), type_right, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
|
||||
#define BINARY_LOOP_COMPLEX_EQUAL3(results, array, type_right, larray, lstrides, rarray, rstrides)\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
BINARY_LOOP_COMPLEX_EQUAL2((results), (array), type_right, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
|
||||
#define BINARY_LOOP_COMPLEX_EQUAL4(results, array, type_right, larray, lstrides, rarray, rstrides)\
|
||||
size_t i = 0;\
|
||||
do {\
|
||||
BINARY_LOOP_COMPLEX_EQUAL3((results), (array), type_right, (larray), (lstrides), (rarray), (rstrides));\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
|
||||
i++;\
|
||||
} while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
|
||||
|
||||
#if ULAB_MAX_DIMS == 1
|
||||
#define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX1
|
||||
#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT1
|
||||
#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE BINARY_LOOP_COMPLEX_RIGHT_DIVIDE1
|
||||
#define BINARY_LOOP_COMPLEX_EQUAL BINARY_LOOP_COMPLEX_EQUAL1
|
||||
#endif /* ULAB_MAX_DIMS == 1 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 2
|
||||
#define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX2
|
||||
#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT2
|
||||
#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE BINARY_LOOP_COMPLEX_RIGHT_DIVIDE2
|
||||
#define BINARY_LOOP_COMPLEX_EQUAL BINARY_LOOP_COMPLEX_EQUAL2
|
||||
#endif /* ULAB_MAX_DIMS == 2 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 3
|
||||
#define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX3
|
||||
#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT3
|
||||
#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE BINARY_LOOP_COMPLEX_RIGHT_DIVIDE3
|
||||
#define BINARY_LOOP_COMPLEX_EQUAL BINARY_LOOP_COMPLEX_EQUAL3
|
||||
#endif /* ULAB_MAX_DIMS == 3 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 4
|
||||
#define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX4
|
||||
#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT4
|
||||
#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE BINARY_LOOP_COMPLEX_RIGHT_DIVIDE4
|
||||
#define BINARY_LOOP_COMPLEX_EQUAL BINARY_LOOP_COMPLEX_EQUAL4
|
||||
#endif /* ULAB_MAX_DIMS == 4 */
|
||||
|
||||
#endif
|
||||
28
code/numpy/carray/carray_tools.c
Normal file
28
code/numpy/carray/carray_tools.c
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2022 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
|
||||
#include "../../ulab.h"
|
||||
#include "../../ndarray.h"
|
||||
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
|
||||
void raise_complex_NotImplementedError(void) {
|
||||
mp_raise_NotImplementedError(MP_ERROR_TEXT("not implemented for complex dtype"));
|
||||
}
|
||||
|
||||
#endif
|
||||
25
code/numpy/carray/carray_tools.h
Normal file
25
code/numpy/carray/carray_tools.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2022 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _CARRAY_TOOLS_
|
||||
#define _CARRAY_TOOLS_
|
||||
|
||||
void raise_complex_NotImplementedError(void);
|
||||
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
#define NOT_IMPLEMENTED_FOR_COMPLEX() raise_complex_NotImplementedError();
|
||||
#define COMPLEX_DTYPE_NOT_IMPLEMENTED(dtype) if((dtype) == NDARRAY_COMPLEX) raise_complex_NotImplementedError();
|
||||
#else
|
||||
#define NOT_IMPLEMENTED_FOR_COMPLEX() // do nothing
|
||||
#define COMPLEX_DTYPE_NOT_IMPLEMENTED(dtype) // do nothing
|
||||
#endif
|
||||
|
||||
#endif
|
||||
643
code/numpy/compare.c
Normal file
643
code/numpy/compare.c
Normal file
|
|
@ -0,0 +1,643 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2021 Zoltán Vörös
|
||||
* 2020 Jeff Epler for Adafruit Industries
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "../ndarray_operators.h"
|
||||
#include "../ulab_tools.h"
|
||||
#include "carray/carray_tools.h"
|
||||
#include "compare.h"
|
||||
|
||||
static mp_obj_t compare_function(mp_obj_t x1, mp_obj_t x2, uint8_t op) {
|
||||
ndarray_obj_t *lhs = ndarray_from_mp_obj(x1, 0);
|
||||
ndarray_obj_t *rhs = ndarray_from_mp_obj(x2, 0);
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
if((lhs->dtype == NDARRAY_COMPLEX) || (rhs->dtype == NDARRAY_COMPLEX)) {
|
||||
NOT_IMPLEMENTED_FOR_COMPLEX()
|
||||
}
|
||||
#endif
|
||||
uint8_t ndim = 0;
|
||||
size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
|
||||
int32_t *lstrides = m_new(int32_t, ULAB_MAX_DIMS);
|
||||
int32_t *rstrides = m_new(int32_t, ULAB_MAX_DIMS);
|
||||
if(!ndarray_can_broadcast(lhs, rhs, &ndim, shape, lstrides, rstrides)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("operands could not be broadcast together"));
|
||||
m_del(size_t, shape, ULAB_MAX_DIMS);
|
||||
m_del(int32_t, lstrides, ULAB_MAX_DIMS);
|
||||
m_del(int32_t, rstrides, ULAB_MAX_DIMS);
|
||||
}
|
||||
|
||||
uint8_t *larray = (uint8_t *)lhs->array;
|
||||
uint8_t *rarray = (uint8_t *)rhs->array;
|
||||
|
||||
if(op == COMPARE_EQUAL) {
|
||||
return ndarray_binary_equality(lhs, rhs, ndim, shape, lstrides, rstrides, MP_BINARY_OP_EQUAL);
|
||||
} else if(op == COMPARE_NOT_EQUAL) {
|
||||
return ndarray_binary_equality(lhs, rhs, ndim, shape, lstrides, rstrides, MP_BINARY_OP_NOT_EQUAL);
|
||||
}
|
||||
// These are the upcasting rules
|
||||
// float always becomes float
|
||||
// operation on identical types preserves type
|
||||
// uint8 + int8 => int16
|
||||
// uint8 + int16 => int16
|
||||
// uint8 + uint16 => uint16
|
||||
// int8 + int16 => int16
|
||||
// int8 + uint16 => uint16
|
||||
// uint16 + int16 => float
|
||||
// The parameters of RUN_COMPARE_LOOP are
|
||||
// typecode of result, type_out, type_left, type_right, lhs operand, rhs operand, operator
|
||||
if(lhs->dtype == NDARRAY_UINT8) {
|
||||
if(rhs->dtype == NDARRAY_UINT8) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_UINT8, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_UINT16, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
} else if(rhs->dtype == NDARRAY_INT16) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
} else if(rhs->dtype == NDARRAY_FLOAT) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, uint8_t, mp_float_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
}
|
||||
} else if(lhs->dtype == NDARRAY_INT8) {
|
||||
if(rhs->dtype == NDARRAY_UINT8) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int8_t, uint8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_INT8, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
} else if(rhs->dtype == NDARRAY_INT16) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
} else if(rhs->dtype == NDARRAY_FLOAT) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, int8_t, mp_float_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
}
|
||||
} else if(lhs->dtype == NDARRAY_UINT16) {
|
||||
if(rhs->dtype == NDARRAY_UINT8) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_UINT16, uint16_t, uint16_t, uint8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_UINT16, uint16_t, uint16_t, int8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_UINT16, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
} else if(rhs->dtype == NDARRAY_INT16) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
} else if(rhs->dtype == NDARRAY_FLOAT) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
}
|
||||
} else if(lhs->dtype == NDARRAY_INT16) {
|
||||
if(rhs->dtype == NDARRAY_UINT8) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int16_t, uint8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int16_t, int8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, int16_t, uint16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
} else if(rhs->dtype == NDARRAY_INT16) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
} else if(rhs->dtype == NDARRAY_FLOAT) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, int16_t, mp_float_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
}
|
||||
} else if(lhs->dtype == NDARRAY_FLOAT) {
|
||||
if(rhs->dtype == NDARRAY_UINT8) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, uint8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
} else if(rhs->dtype == NDARRAY_INT8) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, int8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
} else if(rhs->dtype == NDARRAY_UINT16) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, uint16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
} else if(rhs->dtype == NDARRAY_INT16) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, int16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
} else if(rhs->dtype == NDARRAY_FLOAT) {
|
||||
RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, mp_float_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
|
||||
}
|
||||
}
|
||||
return mp_const_none; // we should never reach this point
|
||||
}
|
||||
|
||||
#if ULAB_NUMPY_HAS_EQUAL | ULAB_NUMPY_HAS_NOTEQUAL
|
||||
static mp_obj_t compare_equal_helper(mp_obj_t x1, mp_obj_t x2, uint8_t comptype) {
|
||||
// scalar comparisons should return a single object of mp_obj_t type
|
||||
mp_obj_t result = compare_function(x1, x2, comptype);
|
||||
if((mp_obj_is_int(x1) || mp_obj_is_float(x1)) && (mp_obj_is_int(x2) || mp_obj_is_float(x2))) {
|
||||
mp_obj_iter_buf_t iter_buf;
|
||||
mp_obj_t iterable = mp_getiter(result, &iter_buf);
|
||||
mp_obj_t item = mp_iternext(iterable);
|
||||
return item;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_CLIP
|
||||
//| def clip(
|
||||
//| a: _ScalarOrArrayLike,
|
||||
//| a_min: _ScalarOrArrayLike,
|
||||
//| a_max: _ScalarOrArrayLike,
|
||||
//| ) -> _ScalarOrNdArray:
|
||||
//| """
|
||||
//| Clips (limits) the values in an array.
|
||||
//|
|
||||
//| :param a: Scalar or array containing elements to clip.
|
||||
//| :param a_min: Minimum value, it will be broadcast against ``a``.
|
||||
//| :param a_max: Maximum value, it will be broadcast against ``a``.
|
||||
//| :return:
|
||||
//| A scalar or array with the elements of ``a``, but where
|
||||
//| values < ``a_min`` are replaced with ``a_min``, and those
|
||||
//| > ``a_max`` with ``a_max``.
|
||||
//| """
|
||||
//| ...
|
||||
mp_obj_t compare_clip(mp_obj_t x1, mp_obj_t x2, mp_obj_t x3) {
|
||||
// Note: this function could be made faster by implementing a single-loop comparison in
|
||||
// RUN_COMPARE_LOOP. However, that would add around 2 kB of compile size, while we
|
||||
// would not gain a factor of two in speed, since the two comparisons should still be
|
||||
// evaluated. In contrast, calling the function twice adds only 140 bytes to the firmware
|
||||
if(mp_obj_is_int(x1) || mp_obj_is_float(x1)) {
|
||||
mp_float_t v1 = mp_obj_get_float(x1);
|
||||
mp_float_t v2 = mp_obj_get_float(x2);
|
||||
mp_float_t v3 = mp_obj_get_float(x3);
|
||||
if(v1 < v2) {
|
||||
return x2;
|
||||
} else if(v1 > v3) {
|
||||
return x3;
|
||||
} else {
|
||||
return x1;
|
||||
}
|
||||
} else { // assume ndarrays
|
||||
return compare_function(x2, compare_function(x1, x3, COMPARE_MINIMUM), COMPARE_MAXIMUM);
|
||||
}
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_3(compare_clip_obj, compare_clip);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_EQUAL
|
||||
//| def equal(x: _ScalarOrArrayLike, y: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """
|
||||
//| Returns ``x == y`` element-wise.
|
||||
//|
|
||||
//| :param x, y:
|
||||
//| Input scalar or array. If ``x.shape != y.shape`` they must
|
||||
//| be broadcastable to a common shape (which becomes the
|
||||
//| shape of the output.)
|
||||
//| :return:
|
||||
//| A boolean scalar or array with the element-wise result of ``x == y``.
|
||||
//| """
|
||||
//| ...
|
||||
mp_obj_t compare_equal(mp_obj_t x1, mp_obj_t x2) {
|
||||
return compare_equal_helper(x1, x2, COMPARE_EQUAL);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(compare_equal_obj, compare_equal);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_NOTEQUAL
|
||||
//| def not_equal(
|
||||
//| x: _ScalarOrArrayLike,
|
||||
//| y: _ScalarOrArrayLike,
|
||||
//| ) -> Union[_bool, ulab.numpy.ndarray]:
|
||||
//| """
|
||||
//| Returns ``x != y`` element-wise.
|
||||
//|
|
||||
//| :param x, y:
|
||||
//| Input scalar or array. If ``x.shape != y.shape`` they must
|
||||
//| be broadcastable to a common shape (which becomes the
|
||||
//| shape of the output.)
|
||||
//| :return:
|
||||
//| A boolean scalar or array with the element-wise result of ``x != y``.
|
||||
//| """
|
||||
//| ...
|
||||
mp_obj_t compare_not_equal(mp_obj_t x1, mp_obj_t x2) {
|
||||
return compare_equal_helper(x1, x2, COMPARE_NOT_EQUAL);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(compare_not_equal_obj, compare_not_equal);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_ISFINITE | ULAB_NUMPY_HAS_ISINF
|
||||
static mp_obj_t compare_isinf_isfinite(mp_obj_t _x, uint8_t mask) {
|
||||
// mask should signify, whether the function is called from isinf (mask = 1),
|
||||
// or from isfinite (mask = 0)
|
||||
if(mp_obj_is_int(_x)) {
|
||||
if(mask) {
|
||||
return mp_const_false;
|
||||
} else {
|
||||
return mp_const_true;
|
||||
}
|
||||
} else if(mp_obj_is_float(_x)) {
|
||||
mp_float_t x = mp_obj_get_float(_x);
|
||||
if(isnan(x)) {
|
||||
return mp_const_false;
|
||||
}
|
||||
if(mask) { // called from isinf
|
||||
return isinf(x) ? mp_const_true : mp_const_false;
|
||||
} else { // called from isfinite
|
||||
return isinf(x) ? mp_const_false : mp_const_true;
|
||||
}
|
||||
} else if(mp_obj_is_type(_x, &ulab_ndarray_type)) {
|
||||
ndarray_obj_t *x = MP_OBJ_TO_PTR(_x);
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(x->dtype)
|
||||
ndarray_obj_t *results = ndarray_new_dense_ndarray(x->ndim, x->shape, NDARRAY_BOOL);
|
||||
// At this point, results is all False
|
||||
uint8_t *rarray = (uint8_t *)results->array;
|
||||
if(x->dtype != NDARRAY_FLOAT) {
|
||||
// int types can never be infinite...
|
||||
if(!mask) {
|
||||
// ...so flip all values in the array, if the function was called from isfinite
|
||||
memset(rarray, 1, results->len);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(results);
|
||||
}
|
||||
uint8_t *xarray = (uint8_t *)x->array;
|
||||
|
||||
ITERATOR_HEAD();
|
||||
mp_float_t value = *(mp_float_t *)xarray;
|
||||
if(isnan(value)) {
|
||||
*rarray++ = 0;
|
||||
} else {
|
||||
*rarray++ = isinf(value) ? mask : 1 - mask;
|
||||
}
|
||||
ITERATOR_TAIL(x, xarray);
|
||||
return MP_OBJ_FROM_PTR(results);
|
||||
} else {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("wrong input type"));
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_ISFINITE
|
||||
//| def isfinite(x: _ScalarOrNdArray) -> Union[_bool, ulab.numpy.ndarray]:
|
||||
//| """
|
||||
//| Tests element-wise for finiteness (i.e., it should not be infinity or a NaN).
|
||||
//|
|
||||
//| :param x: Input scalar or ndarray.
|
||||
//| :return:
|
||||
//| A boolean scalar or array with True where ``x`` is finite, and
|
||||
//| False otherwise.
|
||||
//| """
|
||||
//| ...
|
||||
mp_obj_t compare_isfinite(mp_obj_t _x) {
|
||||
return compare_isinf_isfinite(_x, 0);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(compare_isfinite_obj, compare_isfinite);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_ISINF
|
||||
//| def isinf(x: _ScalarOrNdArray) -> Union[_bool, ulab.numpy.ndarray]:
|
||||
//| """
|
||||
//| Tests element-wise for positive or negative infinity.
|
||||
//|
|
||||
//| :param x: Input scalar or ndarray.
|
||||
//| :return:
|
||||
//| A boolean scalar or array with True where ``x`` is positive or
|
||||
//| negative infinity, and False otherwise.
|
||||
//| """
|
||||
//| ...
|
||||
mp_obj_t compare_isinf(mp_obj_t _x) {
|
||||
return compare_isinf_isfinite(_x, 1);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(compare_isinf_obj, compare_isinf);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_MAXIMUM
|
||||
//| def maximum(x1: _ScalarOrArrayLike, x2: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """
|
||||
//| Returns the element-wise maximum.
|
||||
//|
|
||||
//| :param x1, x2:
|
||||
//| Input scalar or array. If ``x.shape != y.shape`` they must
|
||||
//| be broadcastable to a common shape (which becomes the
|
||||
//| shape of the output.)
|
||||
//| :return:
|
||||
//| A scalar or array with the element-wise maximum of ``x1`` and ``x2``.
|
||||
//| """
|
||||
//| ...
|
||||
mp_obj_t compare_maximum(mp_obj_t x1, mp_obj_t x2) {
|
||||
// extra round, so that we can return maximum(3, 4) properly
|
||||
mp_obj_t result = compare_function(x1, x2, COMPARE_MAXIMUM);
|
||||
if((mp_obj_is_int(x1) || mp_obj_is_float(x1)) && (mp_obj_is_int(x2) || mp_obj_is_float(x2))) {
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(result);
|
||||
return mp_binary_get_val_array(ndarray->dtype, ndarray->array, 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(compare_maximum_obj, compare_maximum);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_MINIMUM
|
||||
|
||||
//| def minimum(x1: _ScalarOrArrayLike, x2: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """
|
||||
//| Returns the element-wise minimum.
|
||||
//|
|
||||
//| :param x1, x2:
|
||||
//| Input scalar or array. If ``x.shape != y.shape`` they must
|
||||
//| be broadcastable to a common shape (which becomes the
|
||||
//| shape of the output.)
|
||||
//| :return:
|
||||
//| A scalar or array with the element-wise minimum of ``x1`` and ``x2``.
|
||||
//| """
|
||||
//| ...
|
||||
mp_obj_t compare_minimum(mp_obj_t x1, mp_obj_t x2) {
|
||||
// extra round, so that we can return minimum(3, 4) properly
|
||||
mp_obj_t result = compare_function(x1, x2, COMPARE_MINIMUM);
|
||||
if((mp_obj_is_int(x1) || mp_obj_is_float(x1)) && (mp_obj_is_int(x2) || mp_obj_is_float(x2))) {
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(result);
|
||||
return mp_binary_get_val_array(ndarray->dtype, ndarray->array, 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(compare_minimum_obj, compare_minimum);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_NONZERO
|
||||
|
||||
//| def nonzero(x: _ScalarOrArrayLike) -> ulab.numpy.ndarray:
|
||||
//| """
|
||||
//| Returns the indices of elements that are non-zero.
|
||||
//|
|
||||
//| :param x:
|
||||
//| Input scalar or array. If ``x`` is a scalar, it is treated
|
||||
//| as a single-element 1-d array.
|
||||
//| :return:
|
||||
//| An array of indices that are non-zero.
|
||||
//| """
|
||||
//| ...
|
||||
mp_obj_t compare_nonzero(mp_obj_t x) {
|
||||
ndarray_obj_t *ndarray_x = ndarray_from_mp_obj(x, 0);
|
||||
// since ndarray_new_linear_array calls m_new0, the content of zero is a single zero
|
||||
ndarray_obj_t *zero = ndarray_new_linear_array(1, NDARRAY_UINT8);
|
||||
|
||||
uint8_t ndim = 0;
|
||||
size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
|
||||
int32_t *x_strides = m_new(int32_t, ULAB_MAX_DIMS);
|
||||
int32_t *zero_strides = m_new(int32_t, ULAB_MAX_DIMS);
|
||||
// we don't actually have to inspect the outcome of ndarray_can_broadcast,
|
||||
// because the right hand side is a linear array with a single element
|
||||
ndarray_can_broadcast(ndarray_x, zero, &ndim, shape, x_strides, zero_strides);
|
||||
|
||||
// equal_obj is a Boolean ndarray
|
||||
mp_obj_t equal_obj = ndarray_binary_equality(ndarray_x, zero, ndim, shape, x_strides, zero_strides, MP_BINARY_OP_NOT_EQUAL);
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(equal_obj);
|
||||
|
||||
// these are no longer needed, get rid of them
|
||||
m_del(size_t, shape, ULAB_MAX_DIMS);
|
||||
m_del(int32_t, x_strides, ULAB_MAX_DIMS);
|
||||
m_del(int32_t, zero_strides, ULAB_MAX_DIMS);
|
||||
|
||||
uint8_t *array = (uint8_t *)ndarray->array;
|
||||
uint8_t *origin = (uint8_t *)ndarray->array;
|
||||
|
||||
// First, count the number of Trues:
|
||||
uint16_t count = 0;
|
||||
size_t indices[ULAB_MAX_DIMS];
|
||||
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
indices[3] = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
indices[2] = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
indices[1] = 0;
|
||||
do {
|
||||
#endif
|
||||
indices[0] = 0;
|
||||
do {
|
||||
if(*array != 0) {
|
||||
count++;
|
||||
}
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 1];
|
||||
indices[0]++;
|
||||
} while(indices[0] < ndarray->shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
array -= ndarray->strides[ULAB_MAX_DIMS - 1] * ndarray->shape[ULAB_MAX_DIMS-1];
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 2];
|
||||
indices[1]++;
|
||||
} while(indices[1] < ndarray->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
array -= ndarray->strides[ULAB_MAX_DIMS - 2] * ndarray->shape[ULAB_MAX_DIMS-2];
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 3];
|
||||
indices[2]++;
|
||||
} while(indices[2] < ndarray->shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
array -= ndarray->strides[ULAB_MAX_DIMS - 3] * ndarray->shape[ULAB_MAX_DIMS-3];
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 4];
|
||||
indices[3]++;
|
||||
} while(indices[3] < ndarray->shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif
|
||||
|
||||
mp_obj_t *items = m_new(mp_obj_t, ndarray->ndim);
|
||||
uint16_t *arrays[ULAB_MAX_DIMS];
|
||||
|
||||
for(uint8_t i = 0; i < ndarray->ndim; i++) {
|
||||
ndarray_obj_t *item_array = ndarray_new_linear_array(count, NDARRAY_UINT16);
|
||||
uint16_t *iarray = (uint16_t *)item_array->array;
|
||||
arrays[ULAB_MAX_DIMS - 1 - i] = iarray;
|
||||
items[ndarray->ndim - 1 - i] = MP_OBJ_FROM_PTR(item_array);
|
||||
}
|
||||
array = origin;
|
||||
count = 0;
|
||||
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
indices[3] = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
indices[2] = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
indices[1] = 0;
|
||||
do {
|
||||
#endif
|
||||
indices[0] = 0;
|
||||
do {
|
||||
if(*array != 0) {
|
||||
for(uint8_t d = 0; d < ndarray->ndim; d++) {
|
||||
arrays[ULAB_MAX_DIMS - 1 - d][count] = indices[d];
|
||||
}
|
||||
count++;
|
||||
}
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 1];
|
||||
indices[0]++;
|
||||
} while(indices[0] < ndarray->shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
array -= ndarray->strides[ULAB_MAX_DIMS - 1] * ndarray->shape[ULAB_MAX_DIMS-1];
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 2];
|
||||
indices[1]++;
|
||||
} while(indices[1] < ndarray->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
array -= ndarray->strides[ULAB_MAX_DIMS - 2] * ndarray->shape[ULAB_MAX_DIMS-2];
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 3];
|
||||
indices[2]++;
|
||||
} while(indices[2] < ndarray->shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
array -= ndarray->strides[ULAB_MAX_DIMS - 3] * ndarray->shape[ULAB_MAX_DIMS-3];
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 4];
|
||||
indices[3]++;
|
||||
} while(indices[3] < ndarray->shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif
|
||||
|
||||
return mp_obj_new_tuple(ndarray->ndim, items);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(compare_nonzero_obj, compare_nonzero);
|
||||
#endif /* ULAB_NUMPY_HAS_NONZERO */
|
||||
|
||||
#if ULAB_NUMPY_HAS_WHERE
|
||||
|
||||
//| def where(
|
||||
//| condition: _ScalarOrArrayLike,
|
||||
//| x: _ScalarOrArrayLike,
|
||||
//| y: _ScalarOrArrayLike,
|
||||
//| ) -> ulab.numpy.ndarray:
|
||||
//| """
|
||||
//| Returns elements from ``x`` or ``y`` depending on ``condition``.
|
||||
//|
|
||||
//| :param condition:
|
||||
//| Input scalar or array. If an element (or scalar) is truthy,
|
||||
//| the corresponding element from ``x`` is chosen, otherwise
|
||||
//| ``y`` is used. ``condition``, ``x`` and ``y`` must also be
|
||||
//| broadcastable to the same shape (which becomes the output
|
||||
//| shape.)
|
||||
//| :param x, y:
|
||||
//| Input scalar or array.
|
||||
//| :return:
|
||||
//| An array with elements from ``x`` when ``condition`` is
|
||||
//| truthy, and ``y`` elsewhere.
|
||||
//| """
|
||||
//| ...
|
||||
mp_obj_t compare_where(mp_obj_t _condition, mp_obj_t _x, mp_obj_t _y) {
|
||||
// this implementation will work with ndarrays, and scalars only
|
||||
ndarray_obj_t *c = ndarray_from_mp_obj(_condition, 0);
|
||||
ndarray_obj_t *x = ndarray_from_mp_obj(_x, 0);
|
||||
ndarray_obj_t *y = ndarray_from_mp_obj(_y, 0);
|
||||
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(c->dtype)
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(x->dtype)
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(y->dtype)
|
||||
|
||||
int32_t *cstrides = m_new(int32_t, ULAB_MAX_DIMS);
|
||||
int32_t *xstrides = m_new(int32_t, ULAB_MAX_DIMS);
|
||||
int32_t *ystrides = m_new(int32_t, ULAB_MAX_DIMS);
|
||||
|
||||
size_t *oshape = m_new(size_t, ULAB_MAX_DIMS);
|
||||
|
||||
uint8_t ndim;
|
||||
|
||||
// establish the broadcasting conditions first
|
||||
// if any two of the arrays can be broadcast together, then
|
||||
// the three arrays can also be broadcast together
|
||||
if(!ndarray_can_broadcast(c, x, &ndim, oshape, cstrides, ystrides) ||
|
||||
!ndarray_can_broadcast(c, y, &ndim, oshape, cstrides, ystrides) ||
|
||||
!ndarray_can_broadcast(x, y, &ndim, oshape, xstrides, ystrides)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("operands could not be broadcast together"));
|
||||
}
|
||||
|
||||
ndim = MAX(MAX(c->ndim, x->ndim), y->ndim);
|
||||
|
||||
for(uint8_t i = 1; i <= ndim; i++) {
|
||||
cstrides[ULAB_MAX_DIMS - i] = c->shape[ULAB_MAX_DIMS - i] < 2 ? 0 : c->strides[ULAB_MAX_DIMS - i];
|
||||
xstrides[ULAB_MAX_DIMS - i] = x->shape[ULAB_MAX_DIMS - i] < 2 ? 0 : x->strides[ULAB_MAX_DIMS - i];
|
||||
ystrides[ULAB_MAX_DIMS - i] = y->shape[ULAB_MAX_DIMS - i] < 2 ? 0 : y->strides[ULAB_MAX_DIMS - i];
|
||||
oshape[ULAB_MAX_DIMS - i] = MAX(MAX(c->shape[ULAB_MAX_DIMS - i], x->shape[ULAB_MAX_DIMS - i]), y->shape[ULAB_MAX_DIMS - i]);
|
||||
}
|
||||
|
||||
uint8_t out_dtype = ndarray_upcast_dtype(x->dtype, y->dtype);
|
||||
ndarray_obj_t *out = ndarray_new_dense_ndarray(ndim, oshape, out_dtype);
|
||||
|
||||
mp_float_t (*cfunc)(void *) = ndarray_get_float_function(c->dtype);
|
||||
mp_float_t (*xfunc)(void *) = ndarray_get_float_function(x->dtype);
|
||||
mp_float_t (*yfunc)(void *) = ndarray_get_float_function(y->dtype);
|
||||
mp_float_t (*ofunc)(void *, mp_float_t ) = ndarray_set_float_function(out->dtype);
|
||||
|
||||
uint8_t *oarray = (uint8_t *)out->array;
|
||||
uint8_t *carray = (uint8_t *)c->array;
|
||||
uint8_t *xarray = (uint8_t *)x->array;
|
||||
uint8_t *yarray = (uint8_t *)y->array;
|
||||
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t i = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
size_t j = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
do {
|
||||
mp_float_t value;
|
||||
mp_float_t cvalue = cfunc(carray);
|
||||
if(cvalue != MICROPY_FLOAT_CONST(0.0)) {
|
||||
value = xfunc(xarray);
|
||||
} else {
|
||||
value = yfunc(yarray);
|
||||
}
|
||||
ofunc(oarray, value);
|
||||
oarray += out->itemsize;
|
||||
carray += cstrides[ULAB_MAX_DIMS - 1];
|
||||
xarray += xstrides[ULAB_MAX_DIMS - 1];
|
||||
yarray += ystrides[ULAB_MAX_DIMS - 1];
|
||||
l++;
|
||||
} while(l < out->shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
carray -= cstrides[ULAB_MAX_DIMS - 1] * c->shape[ULAB_MAX_DIMS-1];
|
||||
carray += cstrides[ULAB_MAX_DIMS - 2];
|
||||
xarray -= xstrides[ULAB_MAX_DIMS - 1] * x->shape[ULAB_MAX_DIMS-1];
|
||||
xarray += xstrides[ULAB_MAX_DIMS - 2];
|
||||
yarray -= ystrides[ULAB_MAX_DIMS - 1] * y->shape[ULAB_MAX_DIMS-1];
|
||||
yarray += ystrides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < out->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
carray -= cstrides[ULAB_MAX_DIMS - 2] * c->shape[ULAB_MAX_DIMS-2];
|
||||
carray += cstrides[ULAB_MAX_DIMS - 3];
|
||||
xarray -= xstrides[ULAB_MAX_DIMS - 2] * x->shape[ULAB_MAX_DIMS-2];
|
||||
xarray += xstrides[ULAB_MAX_DIMS - 3];
|
||||
yarray -= ystrides[ULAB_MAX_DIMS - 2] * y->shape[ULAB_MAX_DIMS-2];
|
||||
yarray += ystrides[ULAB_MAX_DIMS - 3];
|
||||
j++;
|
||||
} while(j < out->shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
carray -= cstrides[ULAB_MAX_DIMS - 3] * c->shape[ULAB_MAX_DIMS-3];
|
||||
carray += cstrides[ULAB_MAX_DIMS - 4];
|
||||
xarray -= xstrides[ULAB_MAX_DIMS - 3] * x->shape[ULAB_MAX_DIMS-3];
|
||||
xarray += xstrides[ULAB_MAX_DIMS - 4];
|
||||
yarray -= ystrides[ULAB_MAX_DIMS - 3] * y->shape[ULAB_MAX_DIMS-3];
|
||||
yarray += ystrides[ULAB_MAX_DIMS - 4];
|
||||
i++;
|
||||
} while(i < out->shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif
|
||||
return MP_OBJ_FROM_PTR(out);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_3(compare_where_obj, compare_where);
|
||||
#endif
|
||||
151
code/numpy/compare.h
Normal file
151
code/numpy/compare.h
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2021 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _COMPARE_
|
||||
#define _COMPARE_
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "../ndarray.h"
|
||||
|
||||
enum COMPARE_FUNCTION_TYPE {
|
||||
COMPARE_EQUAL,
|
||||
COMPARE_NOT_EQUAL,
|
||||
COMPARE_MINIMUM,
|
||||
COMPARE_MAXIMUM,
|
||||
COMPARE_CLIP,
|
||||
};
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_3(compare_clip_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(compare_equal_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(compare_isfinite_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(compare_isinf_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(compare_minimum_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(compare_maximum_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(compare_nonzero_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(compare_not_equal_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_3(compare_where_obj);
|
||||
|
||||
#if ULAB_MAX_DIMS == 1
|
||||
#define COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*((type_out *)(array)) = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? (type_out)(*((type_left *)(larray))) : (type_out)(*((type_right *)(rarray)));\
|
||||
(array) += (results)->strides[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < results->shape[ULAB_MAX_DIMS - 1]);\
|
||||
return MP_OBJ_FROM_PTR(results);\
|
||||
|
||||
#endif // ULAB_MAX_DIMS == 1
|
||||
|
||||
#if ULAB_MAX_DIMS == 2
|
||||
#define COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*((type_out *)(array)) = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? (type_out)(*((type_left *)(larray))) : (type_out)(*((type_right *)(rarray)));\
|
||||
(array) += (results)->strides[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < results->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < results->shape[ULAB_MAX_DIMS - 2]);\
|
||||
return MP_OBJ_FROM_PTR(results);\
|
||||
|
||||
#endif // ULAB_MAX_DIMS == 2
|
||||
|
||||
#if ULAB_MAX_DIMS == 3
|
||||
#define COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*((type_out *)(array)) = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? (type_out)(*((type_left *)(larray))) : (type_out)(*((type_right *)(rarray)));\
|
||||
(array) += (results)->strides[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < results->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < results->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < results->shape[ULAB_MAX_DIMS - 3]);\
|
||||
return MP_OBJ_FROM_PTR(results);\
|
||||
|
||||
#endif // ULAB_MAX_DIMS == 3
|
||||
|
||||
#if ULAB_MAX_DIMS == 4
|
||||
#define COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
|
||||
size_t i = 0;\
|
||||
do {\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*((type_out *)(array)) = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? (type_out)(*((type_left *)(larray))) : (type_out)(*((type_right *)(rarray)));\
|
||||
(array) += (results)->strides[ULAB_MAX_DIMS - 1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < results->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < results->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < results->shape[ULAB_MAX_DIMS - 3]);\
|
||||
(larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];\
|
||||
(larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
|
||||
(rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];\
|
||||
(rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
|
||||
i++;\
|
||||
} while(i < results->shape[ULAB_MAX_DIMS - 4]);\
|
||||
return MP_OBJ_FROM_PTR(results);\
|
||||
|
||||
#endif // ULAB_MAX_DIMS == 4
|
||||
|
||||
#define RUN_COMPARE_LOOP(dtype, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, ndim, shape, op) do {\
|
||||
ndarray_obj_t *results = ndarray_new_dense_ndarray((ndim), (shape), (dtype));\
|
||||
uint8_t *array = (uint8_t *)results->array;\
|
||||
if((op) == COMPARE_MINIMUM) {\
|
||||
COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, <);\
|
||||
}\
|
||||
if((op) == COMPARE_MAXIMUM) {\
|
||||
COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, >);\
|
||||
}\
|
||||
} while(0)
|
||||
|
||||
#endif
|
||||
1079
code/numpy/create.c
Normal file
1079
code/numpy/create.c
Normal file
File diff suppressed because it is too large
Load diff
89
code/numpy/create.h
Normal file
89
code/numpy/create.h
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
|
||||
* 2019-2021 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _CREATE_
|
||||
#define _CREATE_
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "../ndarray.h"
|
||||
|
||||
#if ULAB_NUMPY_HAS_ARANGE
|
||||
mp_obj_t create_arange(size_t , const mp_obj_t *, mp_map_t *);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(create_arange_obj);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_ASARRAY
|
||||
mp_obj_t create_arange(size_t , const mp_obj_t *, mp_map_t *);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(create_asarray_obj);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_CONCATENATE
|
||||
mp_obj_t create_concatenate(size_t , const mp_obj_t *, mp_map_t *);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(create_concatenate_obj);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_DIAG
|
||||
mp_obj_t create_diag(size_t , const mp_obj_t *, mp_map_t *);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(create_diag_obj);
|
||||
#endif
|
||||
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
#if ULAB_NUMPY_HAS_EYE
|
||||
mp_obj_t create_eye(size_t , const mp_obj_t *, mp_map_t *);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(create_eye_obj);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_FULL
|
||||
mp_obj_t create_full(size_t , const mp_obj_t *, mp_map_t *);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(create_full_obj);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_LINSPACE
|
||||
mp_obj_t create_linspace(size_t , const mp_obj_t *, mp_map_t *);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(create_linspace_obj);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_LOGSPACE
|
||||
mp_obj_t create_logspace(size_t , const mp_obj_t *, mp_map_t *);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(create_logspace_obj);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_ONES
|
||||
mp_obj_t create_ones(size_t , const mp_obj_t *, mp_map_t *);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(create_ones_obj);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_TAKE
|
||||
mp_obj_t create_take(size_t , const mp_obj_t *, mp_map_t *);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(create_take_obj);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_ZEROS
|
||||
mp_obj_t create_zeros(size_t , const mp_obj_t *, mp_map_t *);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(create_zeros_obj);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_FROMBUFFER
|
||||
mp_obj_t create_frombuffer(size_t , const mp_obj_t *, mp_map_t *);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(create_frombuffer_obj);
|
||||
#endif
|
||||
|
||||
#define ARANGE_LOOP(type_, ndarray, len, step, stop) \
|
||||
({\
|
||||
type_ *array = (type_ *)(ndarray)->array;\
|
||||
for (size_t i = 0; i < (len) - 1; i++, (value) += (step)) {\
|
||||
*array++ = (type_)(value);\
|
||||
}\
|
||||
*array = (type_)(stop);\
|
||||
})
|
||||
|
||||
#endif
|
||||
105
code/numpy/fft/fft.c
Normal file
105
code/numpy/fft/fft.c
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2024 Zoltán Vörös
|
||||
* 2020 Scott Shawcroft for Adafruit Industries
|
||||
* 2020 Taku Fukada
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "py/runtime.h"
|
||||
#include "py/builtin.h"
|
||||
#include "py/binary.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/objarray.h"
|
||||
|
||||
#include "../carray/carray_tools.h"
|
||||
#include "fft.h"
|
||||
|
||||
//| """Frequency-domain functions"""
|
||||
//|
|
||||
//| import ulab.numpy
|
||||
//| import ulab.utils
|
||||
|
||||
|
||||
//| def fft(r: ulab.numpy.ndarray, c: Optional[ulab.numpy.ndarray] = None) -> Tuple[ulab.numpy.ndarray, ulab.numpy.ndarray]:
|
||||
//| """
|
||||
//| :param ulab.numpy.ndarray r: A 1-dimension array of values whose size is a power of 2
|
||||
//| :param ulab.numpy.ndarray c: An optional 1-dimension array of values whose size is a power of 2, giving the complex part of the value
|
||||
//| :return tuple (r, c): The real and complex parts of the FFT
|
||||
//|
|
||||
//| Perform a Fast Fourier Transform from the time domain into the frequency domain
|
||||
//|
|
||||
//| See also `ulab.utils.spectrogram`, which computes the magnitude of the fft,
|
||||
//| rather than separately returning its real and imaginary parts."""
|
||||
//| ...
|
||||
//|
|
||||
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
|
||||
static mp_obj_t fft_fft(mp_obj_t arg) {
|
||||
return fft_fft_ifft(arg, FFT_FFT);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(fft_fft_obj, fft_fft);
|
||||
#else
|
||||
static mp_obj_t fft_fft(size_t n_args, const mp_obj_t *args) {
|
||||
if(n_args == 2) {
|
||||
return fft_fft_ifft(n_args, args[0], args[1], FFT_FFT);
|
||||
} else {
|
||||
return fft_fft_ifft(n_args, args[0], mp_const_none, FFT_FFT);
|
||||
}
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_fft_obj, 1, 2, fft_fft);
|
||||
#endif
|
||||
|
||||
//| def ifft(r: ulab.numpy.ndarray, c: Optional[ulab.numpy.ndarray] = None) -> Tuple[ulab.numpy.ndarray, ulab.numpy.ndarray]:
|
||||
//| """
|
||||
//| :param ulab.numpy.ndarray r: A 1-dimension array of values whose size is a power of 2
|
||||
//| :param ulab.numpy.ndarray c: An optional 1-dimension array of values whose size is a power of 2, giving the complex part of the value
|
||||
//| :return tuple (r, c): The real and complex parts of the inverse FFT
|
||||
//|
|
||||
//| Perform an Inverse Fast Fourier Transform from the frequeny domain into the time domain"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
|
||||
static mp_obj_t fft_ifft(mp_obj_t arg) {
|
||||
return fft_fft_ifft(arg, FFT_IFFT);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(fft_ifft_obj, fft_ifft);
|
||||
#else
|
||||
static mp_obj_t fft_ifft(size_t n_args, const mp_obj_t *args) {
|
||||
NOT_IMPLEMENTED_FOR_COMPLEX()
|
||||
if(n_args == 2) {
|
||||
return fft_fft_ifft(n_args, args[0], args[1], FFT_IFFT);
|
||||
} else {
|
||||
return fft_fft_ifft(n_args, args[0], mp_const_none, FFT_IFFT);
|
||||
}
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_ifft_obj, 1, 2, fft_ifft);
|
||||
#endif
|
||||
|
||||
static const mp_rom_map_elem_t ulab_fft_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_fft) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_fft), MP_ROM_PTR(&fft_fft_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ifft), MP_ROM_PTR(&fft_ifft_obj) },
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT(mp_module_ulab_fft_globals, ulab_fft_globals_table);
|
||||
|
||||
const mp_obj_module_t ulab_fft_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_fft_globals,
|
||||
};
|
||||
#if CIRCUITPY_ULAB
|
||||
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy_dot_fft, ulab_fft_module);
|
||||
#endif
|
||||
30
code/numpy/fft/fft.h
Normal file
30
code/numpy/fft/fft.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2021 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _FFT_
|
||||
#define _FFT_
|
||||
|
||||
#include "../../ulab.h"
|
||||
#include "../../ulab_tools.h"
|
||||
#include "../../ndarray.h"
|
||||
#include "fft_tools.h"
|
||||
|
||||
extern const mp_obj_module_t ulab_fft_module;
|
||||
|
||||
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
|
||||
MP_DECLARE_CONST_FUN_OBJ_3(fft_fft_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_3(fft_ifft_obj);
|
||||
#else
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(fft_fft_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(fft_ifft_obj);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
266
code/numpy/fft/fft_tools.c
Normal file
266
code/numpy/fft/fft_tools.c
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2024 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include "py/runtime.h"
|
||||
|
||||
#include "../../ndarray.h"
|
||||
#include "../../ulab_tools.h"
|
||||
#include "../carray/carray_tools.h"
|
||||
#include "fft_tools.h"
|
||||
|
||||
#ifndef MP_PI
|
||||
#define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846)
|
||||
#endif
|
||||
#ifndef MP_E
|
||||
#define MP_E MICROPY_FLOAT_CONST(2.71828182845904523536)
|
||||
#endif
|
||||
|
||||
/* Kernel implementation for the case, when ulab has no complex support
|
||||
|
||||
* The following function takes two arrays, namely, the real and imaginary
|
||||
* parts of a complex array, and calculates the Fourier transform in place.
|
||||
*
|
||||
* The function is basically a modification of four1 from Numerical Recipes,
|
||||
* has no dependencies beyond micropython itself (for the definition of mp_float_t),
|
||||
* and can be used independent of ulab.
|
||||
*/
|
||||
|
||||
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
|
||||
/* Kernel implementation for the complex case. Data are contained in data as
|
||||
|
||||
data[0], data[1], data[2], data[3], .... , data[2n - 2], data[2n-1]
|
||||
real[0], imag[0], real[1], imag[1], .... , real[n-1], imag[n-1]
|
||||
|
||||
In general
|
||||
real[i] = data[2i]
|
||||
imag[i] = data[2i+1]
|
||||
|
||||
*/
|
||||
void fft_kernel(mp_float_t *data, size_t n, int isign) {
|
||||
size_t j, m, mmax, istep;
|
||||
mp_float_t tempr, tempi;
|
||||
mp_float_t wtemp, wr, wpr, wpi, wi, theta;
|
||||
|
||||
j = 0;
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
if (j > i) {
|
||||
SWAP(mp_float_t, data[2*i], data[2*j]);
|
||||
SWAP(mp_float_t, data[2*i+1], data[2*j+1]);
|
||||
}
|
||||
m = n >> 1;
|
||||
while (j >= m && m > 0) {
|
||||
j -= m;
|
||||
m >>= 1;
|
||||
}
|
||||
j += m;
|
||||
}
|
||||
|
||||
mmax = 1;
|
||||
while (n > mmax) {
|
||||
istep = mmax << 1;
|
||||
theta = MICROPY_FLOAT_CONST(-2.0)*isign*MP_PI/istep;
|
||||
wtemp = MICROPY_FLOAT_C_FUN(sin)(MICROPY_FLOAT_CONST(0.5) * theta);
|
||||
wpr = MICROPY_FLOAT_CONST(-2.0) * wtemp * wtemp;
|
||||
wpi = MICROPY_FLOAT_C_FUN(sin)(theta);
|
||||
wr = MICROPY_FLOAT_CONST(1.0);
|
||||
wi = MICROPY_FLOAT_CONST(0.0);
|
||||
for(m = 0; m < mmax; m++) {
|
||||
for(size_t i = m; i < n; i += istep) {
|
||||
j = i + mmax;
|
||||
tempr = wr * data[2*j] - wi * data[2*j+1];
|
||||
tempi = wr * data[2*j+1] + wi * data[2*j];
|
||||
data[2*j] = data[2*i] - tempr;
|
||||
data[2*j+1] = data[2*i+1] - tempi;
|
||||
data[2*i] += tempr;
|
||||
data[2*i+1] += tempi;
|
||||
}
|
||||
wtemp = wr;
|
||||
wr = wr*wpr - wi*wpi + wr;
|
||||
wi = wi*wpr + wtemp*wpi + wi;
|
||||
}
|
||||
mmax = istep;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The following function is a helper interface to the python side.
|
||||
* It has been factored out from fft.c, so that the same argument parsing
|
||||
* routine can be called from utils.spectrogram.
|
||||
*/
|
||||
mp_obj_t fft_fft_ifft(mp_obj_t data_in, uint8_t type) {
|
||||
if(!mp_obj_is_type(data_in, &ulab_ndarray_type)) {
|
||||
mp_raise_NotImplementedError(MP_ERROR_TEXT("FFT is defined for ndarrays only"));
|
||||
}
|
||||
ndarray_obj_t *in = MP_OBJ_TO_PTR(data_in);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
if(in->ndim != 1) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("FFT is implemented for linear arrays only"));
|
||||
}
|
||||
#endif
|
||||
size_t len = in->len;
|
||||
// Check if input is of length of power of 2
|
||||
if((len & (len-1)) != 0) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("input array length must be power of 2"));
|
||||
}
|
||||
|
||||
ndarray_obj_t *out = ndarray_new_linear_array(len, NDARRAY_COMPLEX);
|
||||
mp_float_t *data = (mp_float_t *)out->array;
|
||||
uint8_t *array = (uint8_t *)in->array;
|
||||
|
||||
if(in->dtype == NDARRAY_COMPLEX) {
|
||||
uint8_t sz = 2 * sizeof(mp_float_t);
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
memcpy(data, array, sz);
|
||||
data += 2;
|
||||
array += in->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
} else {
|
||||
mp_float_t (*func)(void *) = ndarray_get_float_function(in->dtype);
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
// real part; the imaginary part is 0, no need to assign
|
||||
*data = func(array);
|
||||
data += 2;
|
||||
array += in->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
}
|
||||
data -= 2 * len;
|
||||
|
||||
if(type == FFT_FFT) {
|
||||
fft_kernel(data, len, 1);
|
||||
} else { // inverse transform
|
||||
fft_kernel(data, len, -1);
|
||||
// TODO: numpy accepts the norm keyword argument
|
||||
for(size_t i = 0; i < 2 * len; i++) {
|
||||
*data++ /= len;
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(out);
|
||||
}
|
||||
#else /* ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE */
|
||||
void fft_kernel(mp_float_t *real, mp_float_t *imag, size_t n, int isign) {
|
||||
size_t j, m, mmax, istep;
|
||||
mp_float_t tempr, tempi;
|
||||
mp_float_t wtemp, wr, wpr, wpi, wi, theta;
|
||||
|
||||
j = 0;
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
if (j > i) {
|
||||
SWAP(mp_float_t, real[i], real[j]);
|
||||
SWAP(mp_float_t, imag[i], imag[j]);
|
||||
}
|
||||
m = n >> 1;
|
||||
while (j >= m && m > 0) {
|
||||
j -= m;
|
||||
m >>= 1;
|
||||
}
|
||||
j += m;
|
||||
}
|
||||
|
||||
mmax = 1;
|
||||
while (n > mmax) {
|
||||
istep = mmax << 1;
|
||||
theta = MICROPY_FLOAT_CONST(-2.0)*isign*MP_PI/istep;
|
||||
wtemp = MICROPY_FLOAT_C_FUN(sin)(MICROPY_FLOAT_CONST(0.5) * theta);
|
||||
wpr = MICROPY_FLOAT_CONST(-2.0) * wtemp * wtemp;
|
||||
wpi = MICROPY_FLOAT_C_FUN(sin)(theta);
|
||||
wr = MICROPY_FLOAT_CONST(1.0);
|
||||
wi = MICROPY_FLOAT_CONST(0.0);
|
||||
for(m = 0; m < mmax; m++) {
|
||||
for(size_t i = m; i < n; i += istep) {
|
||||
j = i + mmax;
|
||||
tempr = wr * real[j] - wi * imag[j];
|
||||
tempi = wr * imag[j] + wi * real[j];
|
||||
real[j] = real[i] - tempr;
|
||||
imag[j] = imag[i] - tempi;
|
||||
real[i] += tempr;
|
||||
imag[i] += tempi;
|
||||
}
|
||||
wtemp = wr;
|
||||
wr = wr*wpr - wi*wpi + wr;
|
||||
wi = wi*wpr + wtemp*wpi + wi;
|
||||
}
|
||||
mmax = istep;
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_t fft_fft_ifft(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_im, uint8_t type) {
|
||||
if(!mp_obj_is_type(arg_re, &ulab_ndarray_type)) {
|
||||
mp_raise_NotImplementedError(MP_ERROR_TEXT("FFT is defined for ndarrays only"));
|
||||
}
|
||||
if(n_args == 2) {
|
||||
if(!mp_obj_is_type(arg_im, &ulab_ndarray_type)) {
|
||||
mp_raise_NotImplementedError(MP_ERROR_TEXT("FFT is defined for ndarrays only"));
|
||||
}
|
||||
}
|
||||
ndarray_obj_t *re = MP_OBJ_TO_PTR(arg_re);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
if(re->ndim != 1) {
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(re->dtype)
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("FFT is implemented for linear arrays only"));
|
||||
}
|
||||
#endif
|
||||
size_t len = re->len;
|
||||
// Check if input is of length of power of 2
|
||||
if((len & (len-1)) != 0) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("input array length must be power of 2"));
|
||||
}
|
||||
|
||||
ndarray_obj_t *out_re = ndarray_new_linear_array(len, NDARRAY_FLOAT);
|
||||
mp_float_t *data_re = (mp_float_t *)out_re->array;
|
||||
|
||||
uint8_t *array = (uint8_t *)re->array;
|
||||
mp_float_t (*func)(void *) = ndarray_get_float_function(re->dtype);
|
||||
|
||||
for(size_t i=0; i < len; i++) {
|
||||
*data_re++ = func(array);
|
||||
array += re->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
data_re -= len;
|
||||
ndarray_obj_t *out_im = ndarray_new_linear_array(len, NDARRAY_FLOAT);
|
||||
mp_float_t *data_im = (mp_float_t *)out_im->array;
|
||||
|
||||
if(n_args == 2) {
|
||||
ndarray_obj_t *im = MP_OBJ_TO_PTR(arg_im);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
if(im->ndim != 1) {
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(im->dtype)
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("FFT is implemented for linear arrays only"));
|
||||
}
|
||||
#endif
|
||||
if (re->len != im->len) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("real and imaginary parts must be of equal length"));
|
||||
}
|
||||
array = (uint8_t *)im->array;
|
||||
func = ndarray_get_float_function(im->dtype);
|
||||
for(size_t i=0; i < len; i++) {
|
||||
*data_im++ = func(array);
|
||||
array += im->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
data_im -= len;
|
||||
}
|
||||
|
||||
if(type == FFT_FFT) {
|
||||
fft_kernel(data_re, data_im, len, 1);
|
||||
} else { // inverse transform
|
||||
fft_kernel(data_re, data_im, len, -1);
|
||||
// TODO: numpy accepts the norm keyword argument
|
||||
for(size_t i=0; i < len; i++) {
|
||||
*data_re++ /= len;
|
||||
*data_im++ /= len;
|
||||
}
|
||||
}
|
||||
mp_obj_t tuple[2];
|
||||
tuple[0] = MP_OBJ_FROM_PTR(out_re);
|
||||
tuple[1] = MP_OBJ_FROM_PTR(out_im);
|
||||
return mp_obj_new_tuple(2, tuple);
|
||||
}
|
||||
#endif /* ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE */
|
||||
27
code/numpy/fft/fft_tools.h
Normal file
27
code/numpy/fft/fft_tools.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2021 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _FFT_TOOLS_
|
||||
#define _FFT_TOOLS_
|
||||
|
||||
enum FFT_TYPE {
|
||||
FFT_FFT,
|
||||
FFT_IFFT,
|
||||
};
|
||||
|
||||
#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
|
||||
void fft_kernel(mp_float_t *, size_t , int );
|
||||
mp_obj_t fft_fft_ifft(mp_obj_t , uint8_t );
|
||||
#else
|
||||
void fft_kernel(mp_float_t *, mp_float_t *, size_t , int );
|
||||
mp_obj_t fft_fft_ifft(size_t , mp_obj_t , mp_obj_t , uint8_t );
|
||||
#endif /* ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE */
|
||||
|
||||
#endif /* _FFT_TOOLS_ */
|
||||
132
code/numpy/filter.c
Normal file
132
code/numpy/filter.c
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
|
||||
* 2020 Scott Shawcroft for Adafruit Industries
|
||||
* 2020-2021 Zoltán Vörös
|
||||
* 2020 Taku Fukada
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "../scipy/signal/signal.h"
|
||||
#include "carray/carray_tools.h"
|
||||
#include "filter.h"
|
||||
|
||||
#if ULAB_NUMPY_HAS_CONVOLVE
|
||||
|
||||
mp_obj_t filter_convolve(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_a, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_v, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type) || !mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("convolve arguments must be ndarrays"));
|
||||
}
|
||||
|
||||
ndarray_obj_t *a = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
ndarray_obj_t *c = MP_OBJ_TO_PTR(args[1].u_obj);
|
||||
// deal with linear arrays only
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
if((a->ndim != 1) || (c->ndim != 1)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("convolve arguments must be linear arrays"));
|
||||
}
|
||||
#endif
|
||||
size_t len_a = a->len;
|
||||
size_t len_c = c->len;
|
||||
if(len_a == 0 || len_c == 0) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("convolve arguments must not be empty"));
|
||||
}
|
||||
|
||||
int len = len_a + len_c - 1; // convolve mode "full"
|
||||
int32_t off = len_c - 1;
|
||||
uint8_t dtype = NDARRAY_FLOAT;
|
||||
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
if((a->dtype == NDARRAY_COMPLEX) || (c->dtype == NDARRAY_COMPLEX)) {
|
||||
dtype = NDARRAY_COMPLEX;
|
||||
}
|
||||
#endif
|
||||
ndarray_obj_t *ndarray = ndarray_new_linear_array(len, dtype);
|
||||
mp_float_t *array = (mp_float_t *)ndarray->array;
|
||||
|
||||
uint8_t *aarray = (uint8_t *)a->array;
|
||||
uint8_t *carray = (uint8_t *)c->array;
|
||||
|
||||
int32_t as = a->strides[ULAB_MAX_DIMS - 1] / a->itemsize;
|
||||
int32_t cs = c->strides[ULAB_MAX_DIMS - 1] / c->itemsize;
|
||||
|
||||
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
if(dtype == NDARRAY_COMPLEX) {
|
||||
mp_float_t a_real, a_imag;
|
||||
mp_float_t c_real, c_imag = MICROPY_FLOAT_CONST(0.0);
|
||||
for(int32_t k = -off; k < len-off; k++) {
|
||||
mp_float_t accum_real = MICROPY_FLOAT_CONST(0.0);
|
||||
mp_float_t accum_imag = MICROPY_FLOAT_CONST(0.0);
|
||||
|
||||
int32_t top_n = MIN(len_c, len_a - k);
|
||||
int32_t bot_n = MAX(-k, 0);
|
||||
|
||||
for(int32_t n = bot_n; n < top_n; n++) {
|
||||
int32_t idx_c = (len_c - n - 1) * cs;
|
||||
int32_t idx_a = (n + k) * as;
|
||||
if(a->dtype != NDARRAY_COMPLEX) {
|
||||
a_real = ndarray_get_float_index(aarray, a->dtype, idx_a);
|
||||
a_imag = MICROPY_FLOAT_CONST(0.0);
|
||||
} else {
|
||||
a_real = ndarray_get_float_index(aarray, NDARRAY_FLOAT, 2 * idx_a);
|
||||
a_imag = ndarray_get_float_index(aarray, NDARRAY_FLOAT, 2 * idx_a + 1);
|
||||
}
|
||||
|
||||
if(c->dtype != NDARRAY_COMPLEX) {
|
||||
c_real = ndarray_get_float_index(carray, c->dtype, idx_c);
|
||||
c_imag = MICROPY_FLOAT_CONST(0.0);
|
||||
} else {
|
||||
c_real = ndarray_get_float_index(carray, NDARRAY_FLOAT, 2 * idx_c);
|
||||
c_imag = ndarray_get_float_index(carray, NDARRAY_FLOAT, 2 * idx_c + 1);
|
||||
}
|
||||
accum_real += a_real * c_real - a_imag * c_imag;
|
||||
accum_imag += a_real * c_imag + a_imag * c_real;
|
||||
}
|
||||
*array++ = accum_real;
|
||||
*array++ = accum_imag;
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
#endif
|
||||
|
||||
for(int32_t k = -off; k < len-off; k++) {
|
||||
mp_float_t accum = MICROPY_FLOAT_CONST(0.0);
|
||||
int32_t top_n = MIN(len_c, len_a - k);
|
||||
int32_t bot_n = MAX(-k, 0);
|
||||
for(int32_t n = bot_n; n < top_n; n++) {
|
||||
int32_t idx_c = (len_c - n - 1) * cs;
|
||||
int32_t idx_a = (n + k) * as;
|
||||
mp_float_t ai = ndarray_get_float_index(aarray, a->dtype, idx_a);
|
||||
mp_float_t ci = ndarray_get_float_index(carray, c->dtype, idx_c);
|
||||
accum += ai * ci;
|
||||
}
|
||||
*array++ = accum;
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(filter_convolve_obj, 2, filter_convolve);
|
||||
|
||||
#endif
|
||||
20
code/numpy/filter.h
Normal file
20
code/numpy/filter.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
|
||||
* 2020-2021 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _FILTER_
|
||||
#define _FILTER_
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "../ndarray.h"
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(filter_convolve_obj);
|
||||
#endif
|
||||
806
code/numpy/io/io.c
Normal file
806
code/numpy/io/io.c
Normal file
|
|
@ -0,0 +1,806 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2022 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/builtin.h"
|
||||
#include "py/formatfloat.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/parsenum.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/stream.h"
|
||||
#include "extmod/vfs.h"
|
||||
|
||||
#include "../../ndarray.h"
|
||||
#include "../../ulab_tools.h"
|
||||
#include "io.h"
|
||||
|
||||
#define ULAB_IO_BUFFER_SIZE 128
|
||||
#define ULAB_IO_CLIPBOARD_SIZE 32
|
||||
#define ULAB_IO_MAX_ROWS 65535
|
||||
|
||||
#define ULAB_IO_NULL_ENDIAN 0
|
||||
#define ULAB_IO_LITTLE_ENDIAN 1
|
||||
#define ULAB_IO_BIG_ENDIAN 2
|
||||
|
||||
#if ULAB_NUMPY_HAS_LOAD
|
||||
static void io_read_(mp_obj_t stream, const mp_stream_p_t *stream_p, char *buffer, const char *string, uint16_t len, int *error) {
|
||||
size_t read = stream_p->read(stream, buffer, len, error);
|
||||
bool fail = false;
|
||||
if(read == len) {
|
||||
if(string != NULL) {
|
||||
if(memcmp(buffer, string, len) != 0) {
|
||||
fail = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fail = true;
|
||||
}
|
||||
if(fail) {
|
||||
stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, error);
|
||||
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("corrupted file"));
|
||||
}
|
||||
}
|
||||
|
||||
static mp_obj_t io_load(mp_obj_t file) {
|
||||
if(!mp_obj_is_str(file)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("wrong input type"));
|
||||
}
|
||||
|
||||
int error;
|
||||
char *buffer = m_new(char, ULAB_IO_BUFFER_SIZE);
|
||||
|
||||
// test for endianness
|
||||
uint16_t x = 1;
|
||||
int8_t native_endianness = (x >> 8) == 1 ? ULAB_IO_BIG_ENDIAN : ULAB_IO_LITTLE_ENDIAN;
|
||||
|
||||
mp_obj_t open_args[2] = {
|
||||
file,
|
||||
MP_OBJ_NEW_QSTR(MP_QSTR_rb)
|
||||
};
|
||||
|
||||
mp_obj_t stream = mp_builtin_open_obj.fun.kw(2, open_args, (mp_map_t *)&mp_const_empty_map);
|
||||
const mp_stream_p_t *stream_p = mp_get_stream(stream);
|
||||
|
||||
// read header
|
||||
// magic string
|
||||
io_read_(stream, stream_p, buffer, "\x93NUMPY", 6, &error);
|
||||
// simply discard the version number
|
||||
io_read_(stream, stream_p, buffer, NULL, 2, &error);
|
||||
// header length, represented as a little endian uint16 (0x76, 0x00)
|
||||
io_read_(stream, stream_p, buffer, NULL, 2, &error);
|
||||
|
||||
uint16_t header_length = buffer[1];
|
||||
header_length <<= 8;
|
||||
header_length += buffer[0];
|
||||
|
||||
// beginning of the dictionary describing the array
|
||||
io_read_(stream, stream_p, buffer, "{'descr': '", 11, &error);
|
||||
uint8_t dtype;
|
||||
|
||||
io_read_(stream, stream_p, buffer, NULL, 1, &error);
|
||||
uint8_t endianness = ULAB_IO_NULL_ENDIAN;
|
||||
if(*buffer == '<') {
|
||||
endianness = ULAB_IO_LITTLE_ENDIAN;
|
||||
} else if(*buffer == '>') {
|
||||
endianness = ULAB_IO_BIG_ENDIAN;
|
||||
}
|
||||
|
||||
io_read_(stream, stream_p, buffer, NULL, 2, &error);
|
||||
if(memcmp(buffer, "u1", 2) == 0) {
|
||||
dtype = NDARRAY_UINT8;
|
||||
} else if(memcmp(buffer, "i1", 2) == 0) {
|
||||
dtype = NDARRAY_INT8;
|
||||
} else if(memcmp(buffer, "u2", 2) == 0) {
|
||||
dtype = NDARRAY_UINT16;
|
||||
} else if(memcmp(buffer, "i2", 2) == 0) {
|
||||
dtype = NDARRAY_INT16;
|
||||
}
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
else if(memcmp(buffer, "f4", 2) == 0) {
|
||||
dtype = NDARRAY_FLOAT;
|
||||
}
|
||||
#else
|
||||
else if(memcmp(buffer, "f8", 2) == 0) {
|
||||
dtype = NDARRAY_FLOAT;
|
||||
}
|
||||
#endif
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
else if(memcmp(buffer, "c8", 2) == 0) {
|
||||
dtype = NDARRAY_COMPLEX;
|
||||
}
|
||||
#else
|
||||
else if(memcmp(buffer, "c16", 3) == 0) {
|
||||
dtype = NDARRAY_COMPLEX;
|
||||
}
|
||||
#endif
|
||||
#endif /* ULAB_SUPPORT_COPMLEX */
|
||||
else {
|
||||
stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error);
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("wrong dtype"));
|
||||
}
|
||||
|
||||
io_read_(stream, stream_p, buffer, "', 'fortran_order': False, 'shape': (", 37, &error);
|
||||
|
||||
size_t *shape = m_new0(size_t, ULAB_MAX_DIMS);
|
||||
|
||||
uint16_t bytes_to_read = MIN(ULAB_IO_BUFFER_SIZE, header_length - 51);
|
||||
// bytes_to_read is 128 at most. This should be enough to contain a
|
||||
// maximum of 4 size_t numbers plus the delimiters
|
||||
io_read_(stream, stream_p, buffer, NULL, bytes_to_read, &error);
|
||||
char *needle = buffer;
|
||||
uint8_t ndim = 0;
|
||||
|
||||
// find out the number of dimensions by counting the commas in the string
|
||||
while(1) {
|
||||
if(*needle == ',') {
|
||||
ndim++;
|
||||
if(needle[1] == ')') {
|
||||
break;
|
||||
}
|
||||
} else if((*needle == ')') && (ndim > 0)) {
|
||||
ndim++;
|
||||
break;
|
||||
}
|
||||
needle++;
|
||||
}
|
||||
|
||||
needle = buffer;
|
||||
for(uint8_t i = 0; i < ndim; i++) {
|
||||
size_t number = 0;
|
||||
// trivial number parsing here
|
||||
while(1) {
|
||||
if((*needle == ' ') || (*needle == '\t')) {
|
||||
needle++;
|
||||
}
|
||||
if((*needle > 47) && (*needle < 58)) {
|
||||
number = number * 10 + (*needle - 48);
|
||||
} else if((*needle == ',') || (*needle == ')')) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error);
|
||||
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("corrupted file"));
|
||||
}
|
||||
needle++;
|
||||
}
|
||||
needle++;
|
||||
shape[ULAB_MAX_DIMS - ndim + i] = number;
|
||||
}
|
||||
|
||||
// strip the rest of the header
|
||||
if((bytes_to_read + 51) < header_length) {
|
||||
io_read_(stream, stream_p, buffer, NULL, header_length - (bytes_to_read + 51), &error);
|
||||
}
|
||||
|
||||
ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(ndim, shape, dtype);
|
||||
char *array = (char *)ndarray->array;
|
||||
|
||||
size_t read = stream_p->read(stream, array, ndarray->len * ndarray->itemsize, &error);
|
||||
if(read != ndarray->len * ndarray->itemsize) {
|
||||
stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error);
|
||||
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("corrupted file"));
|
||||
}
|
||||
|
||||
stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error);
|
||||
m_del(char, buffer, ULAB_IO_BUFFER_SIZE);
|
||||
|
||||
// swap the bytes, if necessary
|
||||
if((native_endianness != endianness) && (dtype != NDARRAY_UINT8) && (dtype != NDARRAY_INT8)) {
|
||||
uint8_t sz = ndarray->itemsize;
|
||||
char *tmpbuff = NULL;
|
||||
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
if(dtype == NDARRAY_COMPLEX) {
|
||||
// work with the floating point real and imaginary parts
|
||||
sz /= 2;
|
||||
tmpbuff = m_new(char, sz);
|
||||
for(size_t i = 0; i < ndarray->len; i++) {
|
||||
for(uint8_t k = 0; k < 2; k++) {
|
||||
tmpbuff += sz;
|
||||
for(uint8_t j = 0; j < sz; j++) {
|
||||
memcpy(--tmpbuff, array++, 1);
|
||||
}
|
||||
memcpy(array-sz, tmpbuff, sz);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#endif
|
||||
tmpbuff = m_new(char, sz);
|
||||
for(size_t i = 0; i < ndarray->len; i++) {
|
||||
tmpbuff += sz;
|
||||
for(uint8_t j = 0; j < sz; j++) {
|
||||
memcpy(--tmpbuff, array++, 1);
|
||||
}
|
||||
memcpy(array-sz, tmpbuff, sz);
|
||||
}
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
}
|
||||
#endif
|
||||
m_del(char, tmpbuff, sz);
|
||||
}
|
||||
|
||||
m_del(size_t, shape, ULAB_MAX_DIMS);
|
||||
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(io_load_obj, io_load);
|
||||
#endif /* ULAB_NUMPY_HAS_LOAD */
|
||||
|
||||
#if ULAB_NUMPY_HAS_LOADTXT
|
||||
static void io_assign_value(const char *clipboard, uint8_t len, ndarray_obj_t *ndarray, size_t *idx, uint8_t dtype) {
|
||||
#if MICROPY_PY_BUILTINS_COMPLEX
|
||||
mp_obj_t value = mp_parse_num_decimal(clipboard, len, false, false, NULL);
|
||||
#else
|
||||
mp_obj_t value = mp_parse_num_float(clipboard, len, false, NULL);
|
||||
#endif
|
||||
if(dtype != NDARRAY_FLOAT) {
|
||||
mp_float_t _value = mp_obj_get_float(value);
|
||||
value = mp_obj_new_int((int32_t)MICROPY_FLOAT_C_FUN(round)(_value));
|
||||
}
|
||||
ndarray_set_value(dtype, ndarray->array, (*idx)++, value);
|
||||
}
|
||||
|
||||
static mp_obj_t io_loadtxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_delimiter, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_comments, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_max_rows, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = -1 } },
|
||||
{ MP_QSTR_usecols, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = NDARRAY_FLOAT } },
|
||||
{ MP_QSTR_skiprows, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = 0 } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
mp_obj_t open_args[2] = {
|
||||
args[0].u_obj,
|
||||
MP_OBJ_NEW_QSTR(MP_QSTR_r)
|
||||
};
|
||||
|
||||
mp_obj_t stream = mp_builtin_open_obj.fun.kw(2, open_args, (mp_map_t *)&mp_const_empty_map);
|
||||
const mp_stream_p_t *stream_p = mp_get_stream(stream);
|
||||
|
||||
char *buffer = m_new(char, ULAB_IO_BUFFER_SIZE);
|
||||
int error;
|
||||
|
||||
char delimiter = ' ';
|
||||
if(args[1].u_obj != mp_const_none) {
|
||||
size_t _len;
|
||||
char *_delimiter = m_new(char, 8);
|
||||
_delimiter = (char *)mp_obj_str_get_data(args[1].u_obj, &_len);
|
||||
delimiter = _delimiter[0];
|
||||
}
|
||||
|
||||
char comment_char = '#';
|
||||
if(args[2].u_obj != mp_const_none) {
|
||||
size_t _len;
|
||||
char *_comment_char = m_new(char, 8);
|
||||
_comment_char = (char *)mp_obj_str_get_data(args[2].u_obj, &_len);
|
||||
comment_char = _comment_char[0];
|
||||
}
|
||||
|
||||
uint16_t skiprows = args[6].u_int;
|
||||
uint16_t max_rows = ULAB_IO_MAX_ROWS;
|
||||
if((args[3].u_int > 0) && (args[3].u_int < ULAB_IO_MAX_ROWS)) {
|
||||
max_rows = args[3].u_int + skiprows;
|
||||
}
|
||||
|
||||
uint16_t *cols = NULL;
|
||||
uint8_t used_columns = 0;
|
||||
if(args[4].u_obj != mp_const_none) {
|
||||
if(mp_obj_is_int(args[4].u_obj)) {
|
||||
used_columns = 1;
|
||||
cols = m_new(uint16_t, used_columns);
|
||||
cols[0] = (uint16_t)mp_obj_get_int(args[4].u_obj);
|
||||
} else {
|
||||
#if ULAB_MAX_DIMS == 1
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("usecols keyword must be specified"));
|
||||
#else
|
||||
// assume that the argument is an iterable
|
||||
used_columns = (uint16_t)mp_obj_get_int(mp_obj_len(args[4].u_obj));
|
||||
cols = m_new(uint16_t, used_columns);
|
||||
mp_obj_iter_buf_t iter_buf;
|
||||
mp_obj_t item, iterable = mp_getiter(args[4].u_obj, &iter_buf);
|
||||
while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
*cols++ = (uint16_t)mp_obj_get_int(item);
|
||||
}
|
||||
cols -= used_columns;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t dtype = args[5].u_int;
|
||||
|
||||
// count the columns and rows
|
||||
// we actually count only the rows and the items, and assume that
|
||||
// the number of columns can be gotten by means of a simple division,
|
||||
// i.e., that each row has the same number of columns
|
||||
char *offset;
|
||||
uint16_t rows = 0, items = 0, all_rows = 0;
|
||||
uint8_t read;
|
||||
uint8_t len = 0;
|
||||
|
||||
do {
|
||||
read = (uint8_t)stream_p->read(stream, buffer, ULAB_IO_BUFFER_SIZE - 1, &error);
|
||||
buffer[read] = '\0';
|
||||
offset = buffer;
|
||||
while(*offset != '\0') {
|
||||
while(*offset == comment_char) {
|
||||
// clear the line till the end, or the buffer's end
|
||||
while((*offset != '\0')) {
|
||||
offset++;
|
||||
if(*offset == '\n') {
|
||||
offset++;
|
||||
all_rows++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// catch whitespaces here: if these are not on a comment line, then they delimit a number
|
||||
if(*offset == '\n') {
|
||||
all_rows++;
|
||||
if(all_rows > skiprows) {
|
||||
rows++;
|
||||
items++;
|
||||
len = 0;
|
||||
}
|
||||
if(all_rows == max_rows) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if((*offset == ' ') || (*offset == '\t') || (*offset == '\v') ||
|
||||
(*offset == '\f') || (*offset == '\r') || (*offset == delimiter)) {
|
||||
offset++;
|
||||
while((*offset == ' ') || (*offset == '\t') || (*offset == '\v') || (*offset == '\f') || (*offset == '\r')) {
|
||||
offset++;
|
||||
}
|
||||
if(len > 0) {
|
||||
if(all_rows >= skiprows) {
|
||||
items++;
|
||||
}
|
||||
len = 0;
|
||||
}
|
||||
} else {
|
||||
offset++;
|
||||
len++;
|
||||
}
|
||||
}
|
||||
} while((read > 0) && (all_rows < max_rows));
|
||||
|
||||
if(rows == 0) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("empty file"));
|
||||
}
|
||||
uint16_t columns = items / rows;
|
||||
|
||||
if(columns < used_columns) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("usecols is too high"));
|
||||
}
|
||||
|
||||
size_t *shape = m_new0(size_t, ULAB_MAX_DIMS);
|
||||
|
||||
#if ULAB_MAX_DIMS == 1
|
||||
shape[0] = rows;
|
||||
ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(1, shape, dtype);
|
||||
#else
|
||||
if(args[4].u_obj == mp_const_none) {
|
||||
shape[ULAB_MAX_DIMS - 1] = columns;
|
||||
} else {
|
||||
shape[ULAB_MAX_DIMS - 1] = used_columns;
|
||||
}
|
||||
shape[ULAB_MAX_DIMS - 2] = rows;
|
||||
ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(2, shape, dtype);
|
||||
#endif
|
||||
|
||||
struct mp_stream_seek_t seek_s;
|
||||
seek_s.offset = 0;
|
||||
seek_s.whence = MP_SEEK_SET;
|
||||
stream_p->ioctl(stream, MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, &error);
|
||||
|
||||
char *clipboard = m_new(char, ULAB_IO_CLIPBOARD_SIZE);
|
||||
char *clipboard_origin = clipboard;
|
||||
|
||||
rows = 0;
|
||||
columns = 0;
|
||||
len = 0;
|
||||
|
||||
size_t idx = 0;
|
||||
do {
|
||||
read = stream_p->read(stream, buffer, ULAB_IO_BUFFER_SIZE - 1, &error);
|
||||
buffer[read] = '\0';
|
||||
offset = buffer;
|
||||
|
||||
while(*offset != '\0') {
|
||||
while(*offset == comment_char) {
|
||||
// clear the line till the end, or the buffer's end
|
||||
while((*offset != '\0')) {
|
||||
offset++;
|
||||
if(*offset == '\n') {
|
||||
rows++;
|
||||
offset++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(rows == max_rows) {
|
||||
break;
|
||||
}
|
||||
|
||||
if((*offset == ' ') || (*offset == '\t') || (*offset == '\v') ||
|
||||
(*offset == '\f') || (*offset == '\r') || (*offset == '\n') || (*offset == delimiter)) {
|
||||
offset++;
|
||||
while((*offset == ' ') || (*offset == '\t') || (*offset == '\v') ||
|
||||
(*offset == '\f') || (*offset == '\r') || (*offset == '\n')) {
|
||||
offset++;
|
||||
}
|
||||
if(len > 0) {
|
||||
clipboard = clipboard_origin;
|
||||
if(rows >= skiprows) {
|
||||
#if ULAB_MAX_DIMS == 1
|
||||
if(columns == cols[0]) {
|
||||
io_assign_value(clipboard, len, ndarray, &idx, dtype);
|
||||
}
|
||||
#else
|
||||
if(args[4].u_obj == mp_const_none) {
|
||||
io_assign_value(clipboard, len, ndarray, &idx, dtype);
|
||||
} else {
|
||||
for(uint8_t c = 0; c < used_columns; c++) {
|
||||
if(columns == cols[c]) {
|
||||
io_assign_value(clipboard, len, ndarray, &idx, dtype);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
columns++;
|
||||
len = 0;
|
||||
|
||||
if(offset[-1] == '\n') {
|
||||
columns = 0;
|
||||
rows++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
*clipboard++ = *offset++;
|
||||
len++;
|
||||
}
|
||||
}
|
||||
} while((read > 0) && (rows < max_rows));
|
||||
|
||||
stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error);
|
||||
|
||||
m_del(size_t, shape, ULAB_MAX_DIMS);
|
||||
m_del(char, buffer, ULAB_IO_BUFFER_SIZE);
|
||||
m_del(char, clipboard, ULAB_IO_CLIPBOARD_SIZE);
|
||||
m_del(uint16_t, cols, used_columns);
|
||||
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(io_loadtxt_obj, 1, io_loadtxt);
|
||||
#endif /* ULAB_NUMPY_HAS_LOADTXT */
|
||||
|
||||
|
||||
#if ULAB_NUMPY_HAS_SAVE
|
||||
static uint8_t io_sprintf(char *buffer, const char *comma, size_t x) {
|
||||
uint8_t offset = 1;
|
||||
char *buf = buffer;
|
||||
// our own minimal implementation of sprintf for size_t types
|
||||
// this is required on systems, where sprintf is not available
|
||||
|
||||
// find out, how many characters are required
|
||||
// we could call log10 here...
|
||||
for(size_t i = 10; i < 100000000; i *= 10) {
|
||||
if(x < i) {
|
||||
break;
|
||||
}
|
||||
buf++;
|
||||
}
|
||||
|
||||
while(x > 0) {
|
||||
uint8_t rem = x % 10;
|
||||
*buf-- = '0' + rem;
|
||||
x /= 10;
|
||||
offset++;
|
||||
}
|
||||
|
||||
buf += offset;
|
||||
while(*comma != '\0') {
|
||||
*buf++ = *comma++;
|
||||
offset++;
|
||||
}
|
||||
return offset - 1;
|
||||
}
|
||||
|
||||
static mp_obj_t io_save(mp_obj_t file, mp_obj_t ndarray_) {
|
||||
if(!mp_obj_is_str(file) || !mp_obj_is_type(ndarray_, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("wrong input type"));
|
||||
}
|
||||
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(ndarray_);
|
||||
int error;
|
||||
char *buffer = m_new(char, ULAB_IO_BUFFER_SIZE);
|
||||
uint8_t offset = 0;
|
||||
|
||||
// test for endianness
|
||||
uint16_t x = 1;
|
||||
int8_t native_endianness = (x >> 8) == 1 ? '>' : '<';
|
||||
|
||||
mp_obj_t open_args[2] = {
|
||||
file,
|
||||
MP_OBJ_NEW_QSTR(MP_QSTR_wb)
|
||||
};
|
||||
|
||||
mp_obj_t stream = mp_builtin_open_obj.fun.kw(2, open_args, (mp_map_t *)&mp_const_empty_map);
|
||||
const mp_stream_p_t *stream_p = mp_get_stream(stream);
|
||||
|
||||
// write header;
|
||||
// magic string + header length, which is always 128 - 10 = 118, represented as a little endian uint16 (0x76, 0x00)
|
||||
// + beginning of the dictionary describing the array
|
||||
memcpy(buffer, "\x93NUMPY\x01\x00\x76\x00{'descr': '", 21);
|
||||
offset += 21;
|
||||
|
||||
buffer[offset] = native_endianness;
|
||||
if((ndarray->dtype == NDARRAY_UINT8) || (ndarray->dtype == NDARRAY_INT8)) {
|
||||
// for single-byte data, the endianness doesn't matter
|
||||
buffer[offset] = '|';
|
||||
}
|
||||
offset++;
|
||||
switch(ndarray->dtype) {
|
||||
case NDARRAY_UINT8:
|
||||
memcpy(buffer+offset, "u1", 2);
|
||||
break;
|
||||
case NDARRAY_INT8:
|
||||
memcpy(buffer+offset, "i1", 2);
|
||||
break;
|
||||
case NDARRAY_UINT16:
|
||||
memcpy(buffer+offset, "u2", 2);
|
||||
break;
|
||||
case NDARRAY_INT16:
|
||||
memcpy(buffer+offset, "i2", 2);
|
||||
break;
|
||||
case NDARRAY_FLOAT:
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
memcpy(buffer+offset, "f4", 2);
|
||||
#else
|
||||
memcpy(buffer+offset, "f8", 2);
|
||||
#endif
|
||||
break;
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
case NDARRAY_COMPLEX:
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
memcpy(buffer+offset, "c8", 2);
|
||||
#else
|
||||
memcpy(buffer+offset, "c16", 3);
|
||||
offset++;
|
||||
#endif
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
offset += 2;
|
||||
memcpy(buffer+offset, "', 'fortran_order': False, 'shape': (", 37);
|
||||
offset += 37;
|
||||
|
||||
if(ndarray->ndim == 1) {
|
||||
offset += io_sprintf(buffer+offset, ",\0", ndarray->shape[ULAB_MAX_DIMS - 1]);
|
||||
} else {
|
||||
for(uint8_t i = ndarray->ndim; i > 1; i--) {
|
||||
offset += io_sprintf(buffer+offset, ", \0", ndarray->shape[ULAB_MAX_DIMS - i]);
|
||||
}
|
||||
offset += io_sprintf(buffer+offset, "\0", ndarray->shape[ULAB_MAX_DIMS - 1]);
|
||||
}
|
||||
memcpy(buffer+offset, "), }", 4);
|
||||
offset += 4;
|
||||
// pad with space till the very end
|
||||
memset(buffer+offset, 32, ULAB_IO_BUFFER_SIZE - offset - 1);
|
||||
buffer[ULAB_IO_BUFFER_SIZE - 1] = '\n';
|
||||
stream_p->write(stream, buffer, ULAB_IO_BUFFER_SIZE, &error);
|
||||
|
||||
// write the array data
|
||||
uint8_t sz = ndarray->itemsize;
|
||||
offset = 0;
|
||||
|
||||
uint8_t *array = (uint8_t *)ndarray->array;
|
||||
|
||||
ITERATOR_HEAD();
|
||||
memcpy(buffer+offset, array, sz);
|
||||
offset += sz;
|
||||
if(offset == ULAB_IO_BUFFER_SIZE) {
|
||||
stream_p->write(stream, buffer, offset, &error);
|
||||
offset = 0;
|
||||
}
|
||||
ITERATOR_TAIL(ndarray, array);
|
||||
stream_p->write(stream, buffer, offset, &error);
|
||||
stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error);
|
||||
|
||||
m_del(char, buffer, ULAB_IO_BUFFER_SIZE);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(io_save_obj, io_save);
|
||||
#endif /* ULAB_NUMPY_HAS_SAVE */
|
||||
|
||||
#if ULAB_NUMPY_HAS_SAVETXT
|
||||
static int8_t io_format_float(ndarray_obj_t *ndarray, mp_float_t (*func)(void *), uint8_t *array, char *buffer, const char *delimiter) {
|
||||
// own implementation of float formatting for platforms that don't have sprintf
|
||||
int8_t offset = 0;
|
||||
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C
|
||||
const int precision = 6;
|
||||
#else
|
||||
const int precision = 7;
|
||||
#endif
|
||||
#else
|
||||
const int precision = 16;
|
||||
#endif
|
||||
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
if(ndarray->dtype == NDARRAY_COMPLEX) {
|
||||
mp_float_t real = func(array);
|
||||
mp_float_t imag = func(array + ndarray->itemsize / 2);
|
||||
offset = mp_format_float(real, buffer, ULAB_IO_BUFFER_SIZE, 'f', precision, 'j');
|
||||
if(imag >= MICROPY_FLOAT_CONST(0.0)) {
|
||||
buffer[offset++] = '+';
|
||||
} else {
|
||||
buffer[offset++] = '-';
|
||||
}
|
||||
offset += mp_format_float(-imag, &buffer[offset], ULAB_IO_BUFFER_SIZE, 'f', precision, 'j');
|
||||
}
|
||||
#endif
|
||||
offset = (uint8_t)mp_format_float(func(array), buffer, ULAB_IO_BUFFER_SIZE, 'f', precision, '\0');
|
||||
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
if(ndarray->dtype != NDARRAY_COMPLEX) {
|
||||
// complexes end with a 'j', floats with a '\0', so we have to wind back by one character
|
||||
offset--;
|
||||
}
|
||||
#endif
|
||||
|
||||
while(*delimiter != '\0') {
|
||||
buffer[offset++] = *delimiter++;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static mp_obj_t io_savetxt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_delimiter, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_header, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_footer, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_comments, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
if(!mp_obj_is_str(args[0].u_obj) || !mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("wrong input type"));
|
||||
}
|
||||
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[1].u_obj);
|
||||
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
if(ndarray->ndim > 2) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("array has too many dimensions"));
|
||||
}
|
||||
#endif
|
||||
|
||||
mp_obj_t open_args[2] = {
|
||||
args[0].u_obj,
|
||||
MP_OBJ_NEW_QSTR(MP_QSTR_w)
|
||||
};
|
||||
|
||||
mp_obj_t stream = mp_builtin_open_obj.fun.kw(2, open_args, (mp_map_t *)&mp_const_empty_map);
|
||||
const mp_stream_p_t *stream_p = mp_get_stream(stream);
|
||||
|
||||
char *buffer = m_new(char, ULAB_IO_BUFFER_SIZE);
|
||||
int error;
|
||||
|
||||
size_t len_comment;
|
||||
char *comments;
|
||||
|
||||
if(mp_obj_is_str(args[5].u_obj)) {
|
||||
const char *_comments = mp_obj_str_get_data(args[5].u_obj, &len_comment);
|
||||
comments = (char *)_comments;
|
||||
} else {
|
||||
len_comment = 2;
|
||||
comments = m_new(char, len_comment);
|
||||
comments[0] = '#';
|
||||
comments[1] = ' ';
|
||||
}
|
||||
|
||||
if(mp_obj_is_str(args[3].u_obj)) {
|
||||
size_t _len;
|
||||
const char *header = mp_obj_str_get_data(args[3].u_obj, &_len);
|
||||
|
||||
stream_p->write(stream, comments, len_comment, &error);
|
||||
|
||||
// We can't write the header in the single chunk, for it might contain line breaks
|
||||
for(size_t i = 0; i < _len; header++, i++) {
|
||||
stream_p->write(stream, header, 1, &error);
|
||||
if((*header == '\n') && (i < _len)) {
|
||||
stream_p->write(stream, comments, len_comment, &error);
|
||||
}
|
||||
}
|
||||
stream_p->write(stream, "\n", 1, &error);
|
||||
}
|
||||
|
||||
uint8_t *array = (uint8_t *)ndarray->array;
|
||||
mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype);
|
||||
char *delimiter = m_new(char, 8);
|
||||
|
||||
if(ndarray->ndim == 1) {
|
||||
delimiter[0] = '\n';
|
||||
delimiter[1] = '\0';
|
||||
} else if(args[2].u_obj == mp_const_none) {
|
||||
delimiter[0] = ' ';
|
||||
delimiter[1] = '\0';
|
||||
} else {
|
||||
size_t delimiter_len;
|
||||
delimiter = (char *)mp_obj_str_get_data(args[2].u_obj, &delimiter_len);
|
||||
}
|
||||
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
do {
|
||||
int8_t chars = io_format_float(ndarray, func, array, buffer, l == ndarray->shape[ULAB_MAX_DIMS - 1] - 1 ? "\n" : delimiter);
|
||||
if(chars > 0) {
|
||||
stream_p->write(stream, buffer, chars, &error);
|
||||
}
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 1];
|
||||
l++;
|
||||
} while(l < ndarray->shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
array -= ndarray->strides[ULAB_MAX_DIMS - 1] * ndarray->shape[ULAB_MAX_DIMS-1];
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < ndarray->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif
|
||||
|
||||
if(mp_obj_is_str(args[4].u_obj)) { // footer string
|
||||
size_t _len;
|
||||
const char *footer = mp_obj_str_get_data(args[4].u_obj, &_len);
|
||||
|
||||
stream_p->write(stream, comments, len_comment, &error);
|
||||
|
||||
// We can't write the header in the single chunk, for it might contain line breaks
|
||||
for(size_t i = 0; i < _len; footer++, i++) {
|
||||
stream_p->write(stream, footer, 1, &error);
|
||||
if((*footer == '\n') && (i < _len)) {
|
||||
stream_p->write(stream, comments, len_comment, &error);
|
||||
}
|
||||
}
|
||||
stream_p->write(stream, "\n", 1, &error);
|
||||
}
|
||||
|
||||
stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error);
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(io_savetxt_obj, 2, io_savetxt);
|
||||
#endif /* ULAB_NUMPY_HAS_SAVETXT */
|
||||
19
code/numpy/io/io.h
Normal file
19
code/numpy/io/io.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2022 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _ULAB_IO_
|
||||
#define _ULAB_IO_
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(io_load_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(io_loadtxt_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(io_save_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(io_savetxt_obj);
|
||||
|
||||
#endif
|
||||
542
code/numpy/linalg/linalg.c
Normal file
542
code/numpy/linalg/linalg.c
Normal file
|
|
@ -0,0 +1,542 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2021 Zoltán Vörös
|
||||
* 2020 Scott Shawcroft for Adafruit Industries
|
||||
* 2020 Roberto Colistete Jr.
|
||||
* 2020 Taku Fukada
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
|
||||
#include "../../ulab.h"
|
||||
#include "../../ulab_tools.h"
|
||||
#include "../carray/carray_tools.h"
|
||||
#include "linalg.h"
|
||||
|
||||
#if ULAB_NUMPY_HAS_LINALG_MODULE
|
||||
//|
|
||||
//| import ulab.numpy
|
||||
//|
|
||||
//| """Linear algebra functions"""
|
||||
//|
|
||||
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
//| def cholesky(A: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
|
||||
//| """
|
||||
//| :param ~ulab.numpy.ndarray A: a positive definite, symmetric square matrix
|
||||
//| :return ~ulab.numpy.ndarray L: a square root matrix in the lower triangular form
|
||||
//| :raises ValueError: If the input does not fulfill the necessary conditions
|
||||
//|
|
||||
//| The returned matrix satisfies the equation m=LL*"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t linalg_cholesky(mp_obj_t oin) {
|
||||
ndarray_obj_t *ndarray = tools_object_is_square(oin);
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
|
||||
ndarray_obj_t *L = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, ndarray->shape[ULAB_MAX_DIMS - 1], ndarray->shape[ULAB_MAX_DIMS - 1]), NDARRAY_FLOAT);
|
||||
mp_float_t *Larray = (mp_float_t *)L->array;
|
||||
|
||||
size_t N = ndarray->shape[ULAB_MAX_DIMS - 1];
|
||||
uint8_t *array = (uint8_t *)ndarray->array;
|
||||
mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype);
|
||||
|
||||
for(size_t m=0; m < N; m++) { // rows
|
||||
for(size_t n=0; n < N; n++) { // columns
|
||||
*Larray++ = func(array);
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
array -= ndarray->strides[ULAB_MAX_DIMS - 1] * N;
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 2];
|
||||
}
|
||||
Larray -= N*N;
|
||||
// make sure the matrix is symmetric
|
||||
for(size_t m=0; m < N; m++) { // rows
|
||||
for(size_t n=m+1; n < N; n++) { // columns
|
||||
// compare entry (m, n) to (n, m)
|
||||
if(LINALG_EPSILON < MICROPY_FLOAT_C_FUN(fabs)(Larray[m * N + n] - Larray[n * N + m])) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("input matrix is asymmetric"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this is actually not needed, but Cholesky in numpy returns the lower triangular matrix
|
||||
for(size_t i=0; i < N; i++) { // rows
|
||||
for(size_t j=i+1; j < N; j++) { // columns
|
||||
Larray[i*N + j] = MICROPY_FLOAT_CONST(0.0);
|
||||
}
|
||||
}
|
||||
mp_float_t sum = 0.0;
|
||||
for(size_t i=0; i < N; i++) { // rows
|
||||
for(size_t j=0; j <= i; j++) { // columns
|
||||
sum = Larray[i * N + j];
|
||||
for(size_t k=0; k < j; k++) {
|
||||
sum -= Larray[i * N + k] * Larray[j * N + k];
|
||||
}
|
||||
if(i == j) {
|
||||
if(sum <= MICROPY_FLOAT_CONST(0.0)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("matrix is not positive definite"));
|
||||
} else {
|
||||
Larray[i * N + i] = MICROPY_FLOAT_C_FUN(sqrt)(sum);
|
||||
}
|
||||
} else {
|
||||
Larray[i * N + j] = sum / Larray[j * N + j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(L);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(linalg_cholesky_obj, linalg_cholesky);
|
||||
|
||||
//| def det(m: ulab.numpy.ndarray) -> float:
|
||||
//| """
|
||||
//| :param: m, a square matrix
|
||||
//| :return float: The determinant of the matrix
|
||||
//|
|
||||
//| Computes the eigenvalues and eigenvectors of a square matrix"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t linalg_det(mp_obj_t oin) {
|
||||
ndarray_obj_t *ndarray = tools_object_is_square(oin);
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
|
||||
uint8_t *array = (uint8_t *)ndarray->array;
|
||||
size_t N = ndarray->shape[ULAB_MAX_DIMS - 1];
|
||||
mp_float_t *tmp = m_new(mp_float_t, N * N);
|
||||
for(size_t m=0; m < N; m++) { // rows
|
||||
for(size_t n=0; n < N; n++) { // columns
|
||||
*tmp++ = ndarray_get_float_value(array, ndarray->dtype);
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
array -= ndarray->strides[ULAB_MAX_DIMS - 1] * N;
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 2];
|
||||
}
|
||||
|
||||
// re-wind the pointer
|
||||
tmp -= N*N;
|
||||
|
||||
mp_float_t c;
|
||||
mp_float_t det_sign = 1.0;
|
||||
|
||||
for(size_t m=0; m < N-1; m++){
|
||||
if(MICROPY_FLOAT_C_FUN(fabs)(tmp[m * (N+1)]) < LINALG_EPSILON) {
|
||||
size_t m1 = m + 1;
|
||||
for(; m1 < N; m1++) {
|
||||
if(!(MICROPY_FLOAT_C_FUN(fabs)(tmp[m1*N+m]) < LINALG_EPSILON)) {
|
||||
//look for a line to swap
|
||||
for(size_t m2=0; m2 < N; m2++) {
|
||||
mp_float_t swapVal = tmp[m*N+m2];
|
||||
tmp[m*N+m2] = tmp[m1*N+m2];
|
||||
tmp[m1*N+m2] = swapVal;
|
||||
}
|
||||
det_sign = -det_sign;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (m1 >= N) {
|
||||
m_del(mp_float_t, tmp, N * N);
|
||||
return mp_obj_new_float(0.0);
|
||||
}
|
||||
}
|
||||
for(size_t n=0; n < N; n++) {
|
||||
if(m != n) {
|
||||
c = tmp[N * n + m] / tmp[m * (N+1)];
|
||||
for(size_t k=0; k < N; k++){
|
||||
tmp[N * n + k] -= c * tmp[N * m + k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mp_float_t det = det_sign;
|
||||
|
||||
for(size_t m=0; m < N; m++){
|
||||
det *= tmp[m * (N+1)];
|
||||
}
|
||||
m_del(mp_float_t, tmp, N * N);
|
||||
return mp_obj_new_float(det);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(linalg_det_obj, linalg_det);
|
||||
|
||||
#endif
|
||||
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
//| def eig(m: ulab.numpy.ndarray) -> Tuple[ulab.numpy.ndarray, ulab.numpy.ndarray]:
|
||||
//| """
|
||||
//| :param m: a square matrix
|
||||
//| :return tuple (eigenvectors, eigenvalues):
|
||||
//|
|
||||
//| Computes the eigenvalues and eigenvectors of a square matrix"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t linalg_eig(mp_obj_t oin) {
|
||||
ndarray_obj_t *in = tools_object_is_square(oin);
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(in->dtype)
|
||||
uint8_t *iarray = (uint8_t *)in->array;
|
||||
size_t S = in->shape[ULAB_MAX_DIMS - 1];
|
||||
mp_float_t *array = m_new(mp_float_t, S*S);
|
||||
for(size_t i=0; i < S; i++) { // rows
|
||||
for(size_t j=0; j < S; j++) { // columns
|
||||
*array++ = ndarray_get_float_value(iarray, in->dtype);
|
||||
iarray += in->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
iarray -= in->strides[ULAB_MAX_DIMS - 1] * S;
|
||||
iarray += in->strides[ULAB_MAX_DIMS - 2];
|
||||
}
|
||||
array -= S * S;
|
||||
// make sure the matrix is symmetric
|
||||
for(size_t m=0; m < S; m++) {
|
||||
for(size_t n=m+1; n < S; n++) {
|
||||
// compare entry (m, n) to (n, m)
|
||||
// TODO: this must probably be scaled!
|
||||
if(LINALG_EPSILON < MICROPY_FLOAT_C_FUN(fabs)(array[m * S + n] - array[n * S + m])) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("input matrix is asymmetric"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we got this far, then the matrix will be symmetric
|
||||
|
||||
ndarray_obj_t *eigenvectors = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, S, S), NDARRAY_FLOAT);
|
||||
mp_float_t *eigvectors = (mp_float_t *)eigenvectors->array;
|
||||
|
||||
size_t iterations = linalg_jacobi_rotations(array, eigvectors, S);
|
||||
|
||||
if(iterations == 0) {
|
||||
// the computation did not converge; numpy raises LinAlgError
|
||||
m_del(mp_float_t, array, in->len);
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("iterations did not converge"));
|
||||
}
|
||||
ndarray_obj_t *eigenvalues = ndarray_new_linear_array(S, NDARRAY_FLOAT);
|
||||
mp_float_t *eigvalues = (mp_float_t *)eigenvalues->array;
|
||||
for(size_t i=0; i < S; i++) {
|
||||
eigvalues[i] = array[i * (S + 1)];
|
||||
}
|
||||
m_del(mp_float_t, array, in->len);
|
||||
|
||||
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
|
||||
tuple->items[0] = MP_OBJ_FROM_PTR(eigenvalues);
|
||||
tuple->items[1] = MP_OBJ_FROM_PTR(eigenvectors);
|
||||
return MP_OBJ_FROM_PTR(tuple);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(linalg_eig_obj, linalg_eig);
|
||||
|
||||
//| def inv(m: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
|
||||
//| """
|
||||
//| :param ~ulab.numpy.ndarray m: a square matrix
|
||||
//| :return: The inverse of the matrix, if it exists
|
||||
//| :raises ValueError: if the matrix is not invertible
|
||||
//|
|
||||
//| Computes the inverse of a square matrix"""
|
||||
//| ...
|
||||
//|
|
||||
static mp_obj_t linalg_inv(mp_obj_t o_in) {
|
||||
ndarray_obj_t *ndarray = tools_object_is_square(o_in);
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
|
||||
uint8_t *array = (uint8_t *)ndarray->array;
|
||||
size_t N = ndarray->shape[ULAB_MAX_DIMS - 1];
|
||||
ndarray_obj_t *inverted = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, N, N), NDARRAY_FLOAT);
|
||||
mp_float_t *iarray = (mp_float_t *)inverted->array;
|
||||
|
||||
mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype);
|
||||
|
||||
for(size_t i=0; i < N; i++) { // rows
|
||||
for(size_t j=0; j < N; j++) { // columns
|
||||
*iarray++ = func(array);
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
array -= ndarray->strides[ULAB_MAX_DIMS - 1] * N;
|
||||
array += ndarray->strides[ULAB_MAX_DIMS - 2];
|
||||
}
|
||||
// re-wind the pointer
|
||||
iarray -= N*N;
|
||||
|
||||
if(!linalg_invert_matrix(iarray, N)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("input matrix is singular"));
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(inverted);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(linalg_inv_obj, linalg_inv);
|
||||
#endif
|
||||
|
||||
//| def norm(x: ulab.numpy.ndarray) -> float:
|
||||
//| """
|
||||
//| :param ~ulab.numpy.ndarray x: a vector or a matrix
|
||||
//|
|
||||
//| Computes the 2-norm of a vector or a matrix, i.e., ``sqrt(sum(x*x))``, however, without the RAM overhead."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t linalg_norm(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE} } ,
|
||||
{ MP_QSTR_axis, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
mp_obj_t x = args[0].u_obj;
|
||||
mp_obj_t axis = args[1].u_obj;
|
||||
|
||||
mp_float_t dot = 0.0, value;
|
||||
size_t count = 1;
|
||||
|
||||
if(mp_obj_is_type(x, &mp_type_tuple) || mp_obj_is_type(x, &mp_type_list) || mp_obj_is_type(x, &mp_type_range)) {
|
||||
mp_obj_iter_buf_t iter_buf;
|
||||
mp_obj_t item, iterable = mp_getiter(x, &iter_buf);
|
||||
while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
value = mp_obj_get_float(item);
|
||||
// we could simply take the sum of value ** 2,
|
||||
// but this method is numerically stable
|
||||
dot = dot + (value * value - dot) / count++;
|
||||
}
|
||||
return mp_obj_new_float(MICROPY_FLOAT_C_FUN(sqrt)(dot * (count - 1)));
|
||||
} else if(mp_obj_is_type(x, &ulab_ndarray_type)) {
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(x);
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
|
||||
uint8_t *array = (uint8_t *)ndarray->array;
|
||||
// always get a float, so that we don't have to resolve the dtype later
|
||||
mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype);
|
||||
shape_strides _shape_strides = tools_reduce_axes(ndarray, axis);
|
||||
ndarray_obj_t *results = ndarray_new_dense_ndarray(_shape_strides.ndim, _shape_strides.shape, NDARRAY_FLOAT);
|
||||
mp_float_t *rarray = (mp_float_t *)results->array;
|
||||
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t i = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
size_t j = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
if(axis != mp_const_none) {
|
||||
count = 1;
|
||||
dot = 0.0;
|
||||
}
|
||||
do {
|
||||
value = func(array);
|
||||
dot = dot + (value * value - dot) / count++;
|
||||
array += _shape_strides.strides[0];
|
||||
l++;
|
||||
} while(l < _shape_strides.shape[0]);
|
||||
*rarray = MICROPY_FLOAT_C_FUN(sqrt)(dot * (count - 1));
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
rarray += _shape_strides.increment;
|
||||
array -= _shape_strides.strides[0] * _shape_strides.shape[0];
|
||||
array += _shape_strides.strides[ULAB_MAX_DIMS - 1];
|
||||
k++;
|
||||
} while(k < _shape_strides.shape[ULAB_MAX_DIMS - 1]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
array -= _shape_strides.strides[ULAB_MAX_DIMS - 1] * _shape_strides.shape[ULAB_MAX_DIMS - 1];
|
||||
array += _shape_strides.strides[ULAB_MAX_DIMS - 2];
|
||||
j++;
|
||||
} while(j < _shape_strides.shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
array -= _shape_strides.strides[ULAB_MAX_DIMS - 2] * _shape_strides.shape[ULAB_MAX_DIMS - 2];
|
||||
array += _shape_strides.strides[ULAB_MAX_DIMS - 3];
|
||||
i++;
|
||||
} while(i < _shape_strides.shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif
|
||||
if(results->ndim == 0) {
|
||||
return mp_obj_new_float(*rarray);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(results);
|
||||
}
|
||||
return mp_const_none; // we should never reach this point
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(linalg_norm_obj, 1, linalg_norm);
|
||||
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
//| def qr(m: ulab.numpy.ndarray) -> Tuple[ulab.numpy.ndarray, ulab.numpy.ndarray]:
|
||||
//| """
|
||||
//| :param m: a matrix
|
||||
//| :return tuple (Q, R):
|
||||
//|
|
||||
//| Factor the matrix a as QR, where Q is orthonormal and R is upper-triangular.
|
||||
//| """
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t linalg_qr(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_mode, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_QSTR(MP_QSTR_reduced) } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
|
||||
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("operation is defined for ndarrays only"));
|
||||
}
|
||||
ndarray_obj_t *source = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
if(source->ndim != 2) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("operation is defined for 2D arrays only"));
|
||||
}
|
||||
|
||||
size_t m = source->shape[ULAB_MAX_DIMS - 2]; // rows
|
||||
size_t n = source->shape[ULAB_MAX_DIMS - 1]; // columns
|
||||
|
||||
ndarray_obj_t *Q = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, m, m), NDARRAY_FLOAT);
|
||||
ndarray_obj_t *R = ndarray_new_dense_ndarray(2, source->shape, NDARRAY_FLOAT);
|
||||
|
||||
mp_float_t *qarray = (mp_float_t *)Q->array;
|
||||
mp_float_t *rarray = (mp_float_t *)R->array;
|
||||
|
||||
// simply copy the entries of source to a float array
|
||||
mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype);
|
||||
uint8_t *sarray = (uint8_t *)source->array;
|
||||
|
||||
for(size_t i = 0; i < m; i++) {
|
||||
for(size_t j = 0; j < n; j++) {
|
||||
*rarray++ = func(sarray);
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
sarray -= n * source->strides[ULAB_MAX_DIMS - 1];
|
||||
sarray += source->strides[ULAB_MAX_DIMS - 2];
|
||||
}
|
||||
rarray -= m * n;
|
||||
|
||||
// start with the unit matrix
|
||||
for(size_t i = 0; i < m; i++) {
|
||||
qarray[i * (m + 1)] = 1.0;
|
||||
}
|
||||
|
||||
for(size_t j = 0; j < n; j++) { // columns
|
||||
for(size_t i = m - 1; i > j; i--) { // rows
|
||||
mp_float_t c, s;
|
||||
// Givens matrix: note that numpy uses a strange form of the rotation
|
||||
// [[c s],
|
||||
// [s -c]]
|
||||
if(MICROPY_FLOAT_C_FUN(fabs)(rarray[i * n + j]) < LINALG_EPSILON) { // r[i, j]
|
||||
c = (rarray[(i - 1) * n + j] >= MICROPY_FLOAT_CONST(0.0)) ? MICROPY_FLOAT_CONST(1.0) : MICROPY_FLOAT_CONST(-1.0); // r[i-1, j]
|
||||
s = 0.0;
|
||||
} else if(MICROPY_FLOAT_C_FUN(fabs)(rarray[(i - 1) * n + j]) < LINALG_EPSILON) { // r[i-1, j]
|
||||
c = 0.0;
|
||||
s = (rarray[i * n + j] >= MICROPY_FLOAT_CONST(0.0)) ? MICROPY_FLOAT_CONST(-1.0) : MICROPY_FLOAT_CONST(1.0); // r[i, j]
|
||||
} else {
|
||||
mp_float_t t, u;
|
||||
if(MICROPY_FLOAT_C_FUN(fabs)(rarray[(i - 1) * n + j]) > MICROPY_FLOAT_C_FUN(fabs)(rarray[i * n + j])) { // r[i-1, j], r[i, j]
|
||||
t = rarray[i * n + j] / rarray[(i - 1) * n + j]; // r[i, j]/r[i-1, j]
|
||||
u = MICROPY_FLOAT_C_FUN(sqrt)(1 + t * t);
|
||||
c = MICROPY_FLOAT_CONST(-1.0) / u;
|
||||
s = c * t;
|
||||
} else {
|
||||
t = rarray[(i - 1) * n + j] / rarray[i * n + j]; // r[i-1, j]/r[i, j]
|
||||
u = MICROPY_FLOAT_C_FUN(sqrt)(1 + t * t);
|
||||
s = MICROPY_FLOAT_CONST(-1.0) / u;
|
||||
c = s * t;
|
||||
}
|
||||
}
|
||||
|
||||
mp_float_t r1, r2;
|
||||
// update R: multiply with the rotation matrix from the left
|
||||
for(size_t k = 0; k < n; k++) {
|
||||
r1 = rarray[(i - 1) * n + k]; // r[i-1, k]
|
||||
r2 = rarray[i * n + k]; // r[i, k]
|
||||
rarray[(i - 1) * n + k] = c * r1 + s * r2; // r[i-1, k]
|
||||
rarray[i * n + k] = s * r1 - c * r2; // r[i, k]
|
||||
}
|
||||
|
||||
// update Q: multiply with the transpose of the rotation matrix from the right
|
||||
for(size_t k = 0; k < m; k++) {
|
||||
r1 = qarray[k * m + (i - 1)];
|
||||
r2 = qarray[k * m + i];
|
||||
qarray[k * m + (i - 1)] = c * r1 + s * r2;
|
||||
qarray[k * m + i] = s * r1 - c * r2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
|
||||
GET_STR_DATA_LEN(args[1].u_obj, mode, len);
|
||||
if(memcmp(mode, "complete", 8) == 0) {
|
||||
tuple->items[0] = MP_OBJ_FROM_PTR(Q);
|
||||
tuple->items[1] = MP_OBJ_FROM_PTR(R);
|
||||
} else if(memcmp(mode, "reduced", 7) == 0) {
|
||||
size_t k = MAX(m, n) - MIN(m, n);
|
||||
ndarray_obj_t *q = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, m, m - k), NDARRAY_FLOAT);
|
||||
ndarray_obj_t *r = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, m - k, n), NDARRAY_FLOAT);
|
||||
mp_float_t *qa = (mp_float_t *)q->array;
|
||||
mp_float_t *ra = (mp_float_t *)r->array;
|
||||
for(size_t i = 0; i < m; i++) {
|
||||
memcpy(qa, qarray, (m - k) * q->itemsize);
|
||||
qa += (m - k);
|
||||
qarray += m;
|
||||
}
|
||||
for(size_t i = 0; i < m - k; i++) {
|
||||
memcpy(ra, rarray, n * r->itemsize);
|
||||
ra += n;
|
||||
rarray += n;
|
||||
}
|
||||
tuple->items[0] = MP_OBJ_FROM_PTR(q);
|
||||
tuple->items[1] = MP_OBJ_FROM_PTR(r);
|
||||
} else {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("mode must be complete, or reduced"));
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(tuple);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(linalg_qr_obj, 1, linalg_qr);
|
||||
#endif
|
||||
|
||||
static const mp_rom_map_elem_t ulab_linalg_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_linalg) },
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
#if ULAB_LINALG_HAS_CHOLESKY
|
||||
{ MP_ROM_QSTR(MP_QSTR_cholesky), MP_ROM_PTR(&linalg_cholesky_obj) },
|
||||
#endif
|
||||
#if ULAB_LINALG_HAS_DET
|
||||
{ MP_ROM_QSTR(MP_QSTR_det), MP_ROM_PTR(&linalg_det_obj) },
|
||||
#endif
|
||||
#if ULAB_LINALG_HAS_EIG
|
||||
{ MP_ROM_QSTR(MP_QSTR_eig), MP_ROM_PTR(&linalg_eig_obj) },
|
||||
#endif
|
||||
#if ULAB_LINALG_HAS_INV
|
||||
{ MP_ROM_QSTR(MP_QSTR_inv), MP_ROM_PTR(&linalg_inv_obj) },
|
||||
#endif
|
||||
#if ULAB_LINALG_HAS_QR
|
||||
{ MP_ROM_QSTR(MP_QSTR_qr), MP_ROM_PTR(&linalg_qr_obj) },
|
||||
#endif
|
||||
#endif
|
||||
#if ULAB_LINALG_HAS_NORM
|
||||
{ MP_ROM_QSTR(MP_QSTR_norm), MP_ROM_PTR(&linalg_norm_obj) },
|
||||
#endif
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT(mp_module_ulab_linalg_globals, ulab_linalg_globals_table);
|
||||
|
||||
const mp_obj_module_t ulab_linalg_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_linalg_globals,
|
||||
};
|
||||
#if CIRCUITPY_ULAB
|
||||
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy_dot_linalg, ulab_linalg_module);
|
||||
#endif
|
||||
#endif
|
||||
27
code/numpy/linalg/linalg.h
Normal file
27
code/numpy/linalg/linalg.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2021 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _LINALG_
|
||||
#define _LINALG_
|
||||
|
||||
#include "../../ulab.h"
|
||||
#include "../../ndarray.h"
|
||||
#include "linalg_tools.h"
|
||||
|
||||
extern const mp_obj_module_t ulab_linalg_module;
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(linalg_cholesky_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(linalg_det_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(linalg_eig_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(linalg_inv_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(linalg_norm_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(linalg_qr_obj);
|
||||
#endif
|
||||
170
code/numpy/linalg/linalg_tools.c
Normal file
170
code/numpy/linalg/linalg_tools.c
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2010 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include "py/runtime.h"
|
||||
|
||||
#include "linalg_tools.h"
|
||||
|
||||
/*
|
||||
* The following function inverts a matrix, whose entries are given in the input array
|
||||
* The function has no dependencies beyond micropython itself (for the definition of mp_float_t),
|
||||
* and can be used independent of ulab.
|
||||
*/
|
||||
|
||||
bool linalg_invert_matrix(mp_float_t *data, size_t N) {
|
||||
// returns true, of the inversion was successful,
|
||||
// false, if the matrix is singular
|
||||
|
||||
// initially, this is the unit matrix: the contents of this matrix is what
|
||||
// will be returned after all the transformations
|
||||
mp_float_t *unit = m_new0(mp_float_t, N*N);
|
||||
mp_float_t elem = 1.0;
|
||||
|
||||
for(size_t m=0; m < N; m++) {
|
||||
memcpy(&unit[m * (N+1)], &elem, sizeof(mp_float_t));
|
||||
}
|
||||
for(size_t m=0; m < N; m++){
|
||||
// this could be faster with ((c < epsilon) && (c > -epsilon))
|
||||
if(MICROPY_FLOAT_C_FUN(fabs)(data[m * (N+1)]) < LINALG_EPSILON) {
|
||||
//look for a line to swap
|
||||
size_t m1 = m + 1;
|
||||
for(; m1 < N; m1++) {
|
||||
if(!(MICROPY_FLOAT_C_FUN(fabs)(data[m1*N + m]) < LINALG_EPSILON)) {
|
||||
for(size_t m2=0; m2 < N; m2++) {
|
||||
mp_float_t swapVal = data[m*N+m2];
|
||||
data[m*N+m2] = data[m1*N+m2];
|
||||
data[m1*N+m2] = swapVal;
|
||||
swapVal = unit[m*N+m2];
|
||||
unit[m*N+m2] = unit[m1*N+m2];
|
||||
unit[m1*N+m2] = swapVal;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (m1 >= N) {
|
||||
m_del(mp_float_t, unit, N*N);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for(size_t n=0; n < N; n++) {
|
||||
if(m != n){
|
||||
elem = data[N * n + m] / data[m * (N+1)];
|
||||
for(size_t k=0; k < N; k++) {
|
||||
data[N * n + k] -= elem * data[N * m + k];
|
||||
unit[N * n + k] -= elem * unit[N * m + k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(size_t m=0; m < N; m++) {
|
||||
elem = data[m * (N+1)];
|
||||
for(size_t n=0; n < N; n++) {
|
||||
data[N * m + n] /= elem;
|
||||
unit[N * m + n] /= elem;
|
||||
}
|
||||
}
|
||||
memcpy(data, unit, sizeof(mp_float_t)*N*N);
|
||||
m_del(mp_float_t, unit, N * N);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* The following function calculates the eigenvalues and eigenvectors of a symmetric
|
||||
* real matrix, whose entries are given in the input array.
|
||||
* The function has no dependencies beyond micropython itself (for the definition of mp_float_t),
|
||||
* and can be used independent of ulab.
|
||||
*/
|
||||
|
||||
size_t linalg_jacobi_rotations(mp_float_t *array, mp_float_t *eigvectors, size_t S) {
|
||||
// eigvectors should be a 0-array; start out with the unit matrix
|
||||
for(size_t m=0; m < S; m++) {
|
||||
eigvectors[m * (S+1)] = 1.0;
|
||||
}
|
||||
mp_float_t largest, w, t, c, s, tau, aMk, aNk, vm, vn;
|
||||
size_t M, N;
|
||||
size_t iterations = JACOBI_MAX * S * S;
|
||||
do {
|
||||
iterations--;
|
||||
// find the pivot here
|
||||
M = 0;
|
||||
N = 0;
|
||||
largest = 0.0;
|
||||
for(size_t m=0; m < S-1; m++) { // -1: no need to inspect last row
|
||||
for(size_t n=m+1; n < S; n++) {
|
||||
w = MICROPY_FLOAT_C_FUN(fabs)(array[m * S + n]);
|
||||
if((largest < w) && (LINALG_EPSILON < w)) {
|
||||
M = m;
|
||||
N = n;
|
||||
largest = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(M + N == 0) { // all entries are smaller than epsilon, there is not much we can do...
|
||||
break;
|
||||
}
|
||||
// at this point, we have the pivot, and it is the entry (M, N)
|
||||
// now we have to find the rotation angle
|
||||
w = (array[N * S + N] - array[M * S + M]) / (MICROPY_FLOAT_CONST(2.0)*array[M * S + N]);
|
||||
// The following if/else chooses the smaller absolute value for the tangent
|
||||
// of the rotation angle. Going with the smaller should be numerically stabler.
|
||||
if(w > 0) {
|
||||
t = MICROPY_FLOAT_C_FUN(sqrt)(w*w + MICROPY_FLOAT_CONST(1.0)) - w;
|
||||
} else {
|
||||
t = MICROPY_FLOAT_CONST(-1.0)*(MICROPY_FLOAT_C_FUN(sqrt)(w*w + MICROPY_FLOAT_CONST(1.0)) + w);
|
||||
}
|
||||
s = t / MICROPY_FLOAT_C_FUN(sqrt)(t*t + MICROPY_FLOAT_CONST(1.0)); // the sine of the rotation angle
|
||||
c = MICROPY_FLOAT_CONST(1.0) / MICROPY_FLOAT_C_FUN(sqrt)(t*t + MICROPY_FLOAT_CONST(1.0)); // the cosine of the rotation angle
|
||||
tau = (MICROPY_FLOAT_CONST(1.0)-c)/s; // this is equal to the tangent of the half of the rotation angle
|
||||
|
||||
// at this point, we have the rotation angles, so we can transform the matrix
|
||||
// first the two diagonal elements
|
||||
// a(M, M) = a(M, M) - t*a(M, N)
|
||||
array[M * S + M] = array[M * S + M] - t * array[M * S + N];
|
||||
// a(N, N) = a(N, N) + t*a(M, N)
|
||||
array[N * S + N] = array[N * S + N] + t * array[M * S + N];
|
||||
// after the rotation, the a(M, N), and a(N, M) entries should become zero
|
||||
array[M * S + N] = array[N * S + M] = MICROPY_FLOAT_CONST(0.0);
|
||||
// then all other elements in the column
|
||||
for(size_t k=0; k < S; k++) {
|
||||
if((k == M) || (k == N)) {
|
||||
continue;
|
||||
}
|
||||
aMk = array[M * S + k];
|
||||
aNk = array[N * S + k];
|
||||
// a(M, k) = a(M, k) - s*(a(N, k) + tau*a(M, k))
|
||||
array[M * S + k] -= s * (aNk + tau * aMk);
|
||||
// a(N, k) = a(N, k) + s*(a(M, k) - tau*a(N, k))
|
||||
array[N * S + k] += s * (aMk - tau * aNk);
|
||||
// a(k, M) = a(M, k)
|
||||
array[k * S + M] = array[M * S + k];
|
||||
// a(k, N) = a(N, k)
|
||||
array[k * S + N] = array[N * S + k];
|
||||
}
|
||||
// now we have to update the eigenvectors
|
||||
// the rotation matrix, R, multiplies from the right
|
||||
// R is the unit matrix, except for the
|
||||
// R(M,M) = R(N, N) = c
|
||||
// R(N, M) = s
|
||||
// (M, N) = -s
|
||||
// entries. This means that only the Mth, and Nth columns will change
|
||||
for(size_t m=0; m < S; m++) {
|
||||
vm = eigvectors[m * S + M];
|
||||
vn = eigvectors[m * S + N];
|
||||
// the new value of eigvectors(m, M)
|
||||
eigvectors[m * S + M] = c * vm - s * vn;
|
||||
// the new value of eigvectors(m, N)
|
||||
eigvectors[m * S + N] = s * vm + c * vn;
|
||||
}
|
||||
} while(iterations > 0);
|
||||
|
||||
return iterations;
|
||||
}
|
||||
28
code/numpy/linalg/linalg_tools.h
Normal file
28
code/numpy/linalg/linalg_tools.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2021 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _TOOLS_TOOLS_
|
||||
#define _TOOLS_TOOLS_
|
||||
|
||||
#ifndef LINALG_EPSILON
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
#define LINALG_EPSILON MICROPY_FLOAT_CONST(1.2e-7)
|
||||
#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
|
||||
#define LINALG_EPSILON MICROPY_FLOAT_CONST(2.3e-16)
|
||||
#endif
|
||||
#endif /* LINALG_EPSILON */
|
||||
|
||||
#define JACOBI_MAX 20
|
||||
|
||||
bool linalg_invert_matrix(mp_float_t *, size_t );
|
||||
size_t linalg_jacobi_rotations(mp_float_t *, mp_float_t *, size_t );
|
||||
|
||||
#endif /* _TOOLS_TOOLS_ */
|
||||
|
||||
66
code/numpy/ndarray/ndarray_iter.c
Normal file
66
code/numpy/ndarray/ndarray_iter.c
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Zoltán Vörös
|
||||
*
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
|
||||
#include "ndarray_iter.h"
|
||||
|
||||
#ifdef NDARRAY_HAS_FLATITER
|
||||
mp_obj_t ndarray_flatiter_make_new(mp_obj_t self_in) {
|
||||
ndarray_flatiter_t *flatiter = m_new_obj(ndarray_flatiter_t);
|
||||
flatiter->base.type = &ndarray_flatiter_type;
|
||||
flatiter->iternext = ndarray_flatiter_next;
|
||||
flatiter->ndarray = self_in;
|
||||
flatiter->cur = 0;
|
||||
return MP_OBJ_FROM_PTR(flatiter);
|
||||
}
|
||||
|
||||
mp_obj_t ndarray_flatiter_next(mp_obj_t self_in) {
|
||||
ndarray_flatiter_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(self->ndarray);
|
||||
uint8_t *array = (uint8_t *)ndarray->array;
|
||||
|
||||
if(self->cur < ndarray->len) {
|
||||
uint32_t remainder = self->cur;
|
||||
uint8_t i = ULAB_MAX_DIMS - 1;
|
||||
do {
|
||||
size_t div = (remainder / ndarray->shape[i]);
|
||||
array += remainder * ndarray->strides[i];
|
||||
remainder -= div * ndarray->shape[i];
|
||||
i--;
|
||||
} while(i > ULAB_MAX_DIMS - ndarray->ndim);
|
||||
self->cur++;
|
||||
return ndarray_get_item(ndarray, array);
|
||||
}
|
||||
return MP_OBJ_STOP_ITERATION;
|
||||
}
|
||||
|
||||
mp_obj_t ndarray_new_flatiterator(mp_obj_t flatiter_in, mp_obj_iter_buf_t *iter_buf) {
|
||||
assert(sizeof(ndarray_flatiter_t) <= sizeof(mp_obj_iter_buf_t));
|
||||
ndarray_flatiter_t *iter = (ndarray_flatiter_t *)iter_buf;
|
||||
ndarray_flatiter_t *flatiter = MP_OBJ_TO_PTR(flatiter_in);
|
||||
iter->base.type = &mp_type_polymorph_iter;
|
||||
iter->iternext = ndarray_flatiter_next;
|
||||
iter->ndarray = flatiter->ndarray;
|
||||
iter->cur = 0;
|
||||
return MP_OBJ_FROM_PTR(iter);
|
||||
}
|
||||
|
||||
mp_obj_t ndarray_get_flatiterator(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) {
|
||||
return ndarray_new_flatiterator(o_in, iter_buf);
|
||||
}
|
||||
#endif /* NDARRAY_HAS_FLATITER */
|
||||
36
code/numpy/ndarray/ndarray_iter.h
Normal file
36
code/numpy/ndarray/ndarray_iter.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
|
||||
* 2020-2021 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _NDARRAY_ITER_
|
||||
#define _NDARRAY_ITER_
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/binary.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/objarray.h"
|
||||
|
||||
#include "../../ulab.h"
|
||||
#include "../../ndarray.h"
|
||||
|
||||
// TODO: take simply mp_obj_ndarray_it_t from ndarray.c
|
||||
typedef struct _mp_obj_ndarray_flatiter_t {
|
||||
mp_obj_base_t base;
|
||||
mp_fun_1_t iternext;
|
||||
mp_obj_t ndarray;
|
||||
size_t cur;
|
||||
} ndarray_flatiter_t;
|
||||
|
||||
mp_obj_t ndarray_get_flatiterator(mp_obj_t , mp_obj_iter_buf_t *);
|
||||
mp_obj_t ndarray_flatiter_make_new(mp_obj_t );
|
||||
mp_obj_t ndarray_flatiter_next(mp_obj_t );
|
||||
|
||||
#endif
|
||||
1429
code/numpy/numerical.c
Normal file
1429
code/numpy/numerical.c
Normal file
File diff suppressed because it is too large
Load diff
523
code/numpy/numerical.h
Normal file
523
code/numpy/numerical.h
Normal file
|
|
@ -0,0 +1,523 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2021 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _NUMERICAL_
|
||||
#define _NUMERICAL_
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "../ndarray.h"
|
||||
|
||||
// TODO: implement cumsum
|
||||
|
||||
#define RUN_ARGMIN1(ndarray, type, array, results, rarray, index, op)\
|
||||
({\
|
||||
uint16_t best_index = 0;\
|
||||
type best_value = *((type *)(array));\
|
||||
if(((op) == NUMERICAL_MAX) || ((op) == NUMERICAL_ARGMAX)) {\
|
||||
for(uint16_t i=0; i < (ndarray)->shape[(index)]; i++) {\
|
||||
if(*((type *)(array)) > best_value) {\
|
||||
best_index = i;\
|
||||
best_value = *((type *)(array));\
|
||||
}\
|
||||
(array) += (ndarray)->strides[(index)];\
|
||||
}\
|
||||
} else {\
|
||||
for(uint16_t i=0; i < (ndarray)->shape[(index)]; i++) {\
|
||||
if(*((type *)(array)) < best_value) {\
|
||||
best_index = i;\
|
||||
best_value = *((type *)(array));\
|
||||
}\
|
||||
(array) += (ndarray)->strides[(index)];\
|
||||
}\
|
||||
}\
|
||||
if(((op) == NUMERICAL_ARGMAX) || ((op) == NUMERICAL_ARGMIN)) {\
|
||||
memcpy((rarray), &best_index, (results)->itemsize);\
|
||||
} else {\
|
||||
memcpy((rarray), &best_value, (results)->itemsize);\
|
||||
}\
|
||||
(rarray) += (results)->itemsize;\
|
||||
})
|
||||
|
||||
#define RUN_SUM1(type, array, results, rarray, ss)\
|
||||
({\
|
||||
type sum = 0;\
|
||||
for(size_t i=0; i < (ss).shape[0]; i++) {\
|
||||
sum += *((type *)(array));\
|
||||
(array) += (ss).strides[0];\
|
||||
}\
|
||||
memcpy((rarray), &sum, (results)->itemsize);\
|
||||
(rarray) += (results)->itemsize;\
|
||||
})
|
||||
|
||||
// Instead of the straightforward implementation of the definition,
|
||||
// we take the numerically stable Welford algorithm here
|
||||
// https://www.johndcook.com/blog/2008/09/26/comparing-three-methods-of-computing-standard-deviation/
|
||||
#define RUN_MEAN_STD1(type, array, rarray, ss, div, isStd)\
|
||||
({\
|
||||
mp_float_t M = 0.0, m = 0.0, S = 0.0;\
|
||||
for(size_t i=0; i < (ss).shape[0]; i++) {\
|
||||
mp_float_t value = (mp_float_t)(*(type *)(array));\
|
||||
m = M + (value - M) / (mp_float_t)(i+1);\
|
||||
if(isStd) {\
|
||||
S += (value - M) * (value - m);\
|
||||
}\
|
||||
M = m;\
|
||||
(array) += (ss).strides[0];\
|
||||
}\
|
||||
*(rarray)++ = isStd ? MICROPY_FLOAT_C_FUN(sqrt)(S / (div)) : M;\
|
||||
})
|
||||
|
||||
#define RUN_DIFF1(ndarray, type, array, results, rarray, index, stencil, N)\
|
||||
({\
|
||||
for(size_t i=0; i < (results)->shape[ULAB_MAX_DIMS - 1]; i++) {\
|
||||
type sum = 0;\
|
||||
uint8_t *source = (array);\
|
||||
for(uint8_t d=0; d < (N)+1; d++) {\
|
||||
sum -= (stencil)[d] * *((type *)source);\
|
||||
source += (ndarray)->strides[(index)];\
|
||||
}\
|
||||
(array) += (ndarray)->strides[ULAB_MAX_DIMS - 1];\
|
||||
*(type *)(rarray) = sum;\
|
||||
(rarray) += (results)->itemsize;\
|
||||
}\
|
||||
})
|
||||
|
||||
#define HEAPSORT1(type, array, increment, N)\
|
||||
({\
|
||||
type *_array = (type *)array;\
|
||||
type tmp;\
|
||||
size_t c, q = (N), p, r = (N) >> 1;\
|
||||
for (;;) {\
|
||||
if (r > 0) {\
|
||||
tmp = _array[(--r)*(increment)];\
|
||||
} else {\
|
||||
q--;\
|
||||
if(q == 0) {\
|
||||
break;\
|
||||
}\
|
||||
tmp = _array[q*(increment)];\
|
||||
_array[q*(increment)] = _array[0];\
|
||||
}\
|
||||
p = r;\
|
||||
c = r + r + 1;\
|
||||
while (c < q) {\
|
||||
if((c + 1 < q) && (_array[(c+1)*(increment)] > _array[c*(increment)])) {\
|
||||
c++;\
|
||||
}\
|
||||
if(_array[c*(increment)] > tmp) {\
|
||||
_array[p*(increment)] = _array[c*(increment)];\
|
||||
p = c;\
|
||||
c = p + p + 1;\
|
||||
} else {\
|
||||
break;\
|
||||
}\
|
||||
}\
|
||||
_array[p*(increment)] = tmp;\
|
||||
}\
|
||||
})
|
||||
|
||||
#define HEAP_ARGSORT1(type, array, increment, N, iarray, iincrement)\
|
||||
({\
|
||||
type *_array = (type *)array;\
|
||||
type tmp;\
|
||||
uint16_t itmp, c, q = (N), p, r = (N) >> 1;\
|
||||
assert(N);\
|
||||
for (;;) {\
|
||||
if (r > 0) {\
|
||||
r--;\
|
||||
itmp = (iarray)[r*(iincrement)];\
|
||||
tmp = _array[itmp*(increment)];\
|
||||
} else {\
|
||||
q--;\
|
||||
if(q == 0) {\
|
||||
break;\
|
||||
}\
|
||||
itmp = (iarray)[q*(iincrement)];\
|
||||
tmp = _array[itmp*(increment)];\
|
||||
(iarray)[q*(iincrement)] = (iarray)[0];\
|
||||
}\
|
||||
p = r;\
|
||||
c = r + r + 1;\
|
||||
while (c < q) {\
|
||||
if((c + 1 < q) && (_array[(iarray)[(c+1)*(iincrement)]*(increment)] > _array[(iarray)[c*(iincrement)]*(increment)])) {\
|
||||
c++;\
|
||||
}\
|
||||
if(_array[(iarray)[c*(iincrement)]*(increment)] > tmp) {\
|
||||
(iarray)[p*(iincrement)] = (iarray)[c*(iincrement)];\
|
||||
p = c;\
|
||||
c = p + p + 1;\
|
||||
} else {\
|
||||
break;\
|
||||
}\
|
||||
}\
|
||||
(iarray)[p*(iincrement)] = itmp;\
|
||||
}\
|
||||
})
|
||||
|
||||
#if ULAB_MAX_DIMS == 1
|
||||
#define RUN_SUM(type, array, results, rarray, ss) do {\
|
||||
RUN_SUM1(type, (array), (results), (rarray), (ss));\
|
||||
} while(0)
|
||||
|
||||
#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\
|
||||
RUN_MEAN_STD1(type, (array), (rarray), (ss), (div), (isStd));\
|
||||
} while(0)
|
||||
|
||||
#define RUN_ARGMIN(ndarray, type, array, results, rarray, shape, strides, index, op) do {\
|
||||
RUN_ARGMIN1((ndarray), type, (array), (results), (rarray), (index), (op));\
|
||||
} while(0)
|
||||
|
||||
#define RUN_DIFF(ndarray, type, array, results, rarray, shape, strides, index, stencil, N) do {\
|
||||
RUN_DIFF1((ndarray), type, (array), (results), (rarray), (index), (stencil), (N));\
|
||||
} while(0)
|
||||
|
||||
#define HEAPSORT(ndarray, type, array, shape, strides, index, increment, N) do {\
|
||||
HEAPSORT1(type, (array), (increment), (N));\
|
||||
} while(0)
|
||||
|
||||
#define HEAP_ARGSORT(ndarray, type, array, shape, strides, index, increment, N, iarray, istrides, iincrement) do {\
|
||||
HEAP_ARGSORT1(type, (array), (increment), (N), (iarray), (iincrement));\
|
||||
} while(0)
|
||||
|
||||
#endif
|
||||
|
||||
#if ULAB_MAX_DIMS == 2
|
||||
#define RUN_SUM(type, array, results, rarray, ss) do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
RUN_SUM1(type, (array), (results), (rarray), (ss));\
|
||||
(array) -= (ss).strides[0] * (ss).shape[0];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
|
||||
} while(0)
|
||||
|
||||
#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
RUN_MEAN_STD1(type, (array), (rarray), (ss), (div), (isStd));\
|
||||
(array) -= (ss).strides[0] * (ss).shape[0];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
|
||||
} while(0)
|
||||
|
||||
|
||||
#define RUN_ARGMIN(ndarray, type, array, results, rarray, shape, strides, index, op) do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
RUN_ARGMIN1((ndarray), type, (array), (results), (rarray), (index), (op));\
|
||||
(array) -= (ndarray)->strides[(index)] * (ndarray)->shape[(index)];\
|
||||
(array) += (strides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (shape)[ULAB_MAX_DIMS - 1]);\
|
||||
} while(0)
|
||||
|
||||
#define RUN_DIFF(ndarray, type, array, results, rarray, shape, strides, index, stencil, N) do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
RUN_DIFF1((ndarray), type, (array), (results), (rarray), (index), (stencil), (N));\
|
||||
(array) -= (ndarray)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(array) += (ndarray)->strides[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (results)->strides[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
} while(0)
|
||||
|
||||
#define HEAPSORT(ndarray, type, array, shape, strides, index, increment, N) do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
HEAPSORT1(type, (array), (increment), (N));\
|
||||
(array) += (strides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (shape)[ULAB_MAX_DIMS - 1]);\
|
||||
} while(0)
|
||||
|
||||
#define HEAP_ARGSORT(ndarray, type, array, shape, strides, index, increment, N, iarray, istrides, iincrement) do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
HEAP_ARGSORT1(type, (array), (increment), (N), (iarray), (iincrement));\
|
||||
(array) += (strides)[ULAB_MAX_DIMS - 1];\
|
||||
(iarray) += (istrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (shape)[ULAB_MAX_DIMS - 1]);\
|
||||
} while(0)
|
||||
|
||||
#endif
|
||||
|
||||
#if ULAB_MAX_DIMS == 3
|
||||
#define RUN_SUM(type, array, results, rarray, ss) do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
RUN_SUM1(type, (array), (results), (rarray), (ss));\
|
||||
(array) -= (ss).strides[0] * (ss).shape[0];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
|
||||
(array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
|
||||
} while(0)
|
||||
|
||||
#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
RUN_MEAN_STD1(type, (array), (rarray), (ss), (div), (isStd));\
|
||||
(array) -= (ss).strides[0] * (ss).shape[0];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
|
||||
(array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
|
||||
} while(0)
|
||||
|
||||
#define RUN_ARGMIN(ndarray, type, array, results, rarray, shape, strides, index, op) do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
RUN_ARGMIN1((ndarray), type, (array), (results), (rarray), (index), (op));\
|
||||
(array) -= (ndarray)->strides[(index)] * (ndarray)->shape[(index)];\
|
||||
(array) += (strides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (shape)[ULAB_MAX_DIMS - 1]);\
|
||||
(array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
|
||||
(array) += (strides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (shape)[ULAB_MAX_DIMS - 2]);\
|
||||
} while(0)
|
||||
|
||||
#define RUN_DIFF(ndarray, type, array, results, rarray, shape, strides, index, stencil, N) do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
RUN_DIFF1((ndarray), type, (array), (results), (rarray), (index), (stencil), (N));\
|
||||
(array) -= (ndarray)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(array) += (ndarray)->strides[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (results)->strides[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (shape)[ULAB_MAX_DIMS - 2]);\
|
||||
(array) -= (ndarray)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
|
||||
(array) += (ndarray)->strides[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) += (results)->strides[ULAB_MAX_DIMS - 3];\
|
||||
k++;\
|
||||
} while(k < (shape)[ULAB_MAX_DIMS - 3]);\
|
||||
} while(0)
|
||||
|
||||
#define HEAPSORT(ndarray, type, array, shape, strides, index, increment, N) do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
HEAPSORT1(type, (array), (increment), (N));\
|
||||
(array) += (strides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (shape)[ULAB_MAX_DIMS - 1]);\
|
||||
(array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
|
||||
(array) += (strides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (shape)[ULAB_MAX_DIMS - 2]);\
|
||||
} while(0)
|
||||
|
||||
#define HEAP_ARGSORT(ndarray, type, array, shape, strides, index, increment, N, iarray, istrides, iincrement) do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
HEAP_ARGSORT1(type, (array), (increment), (N), (iarray), (iincrement));\
|
||||
(array) += (strides)[ULAB_MAX_DIMS - 1];\
|
||||
(iarray) += (istrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (shape)[ULAB_MAX_DIMS - 1]);\
|
||||
(iarray) -= (istrides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
|
||||
(iarray) += (istrides)[ULAB_MAX_DIMS - 2];\
|
||||
(array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
|
||||
(array) += (strides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (shape)[ULAB_MAX_DIMS - 2]);\
|
||||
} while(0)
|
||||
|
||||
#endif
|
||||
|
||||
#if ULAB_MAX_DIMS == 4
|
||||
#define RUN_SUM(type, array, results, rarray, ss) do {\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
RUN_SUM1(type, (array), (results), (rarray), (ss));\
|
||||
(array) -= (ss).strides[0] * (ss).shape[0];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
|
||||
(array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
|
||||
(array) -= (ss).strides[ULAB_MAX_DIMS - 2] * (ss).shape[ULAB_MAX_DIMS - 2];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (ss).shape[ULAB_MAX_DIMS - 3]);\
|
||||
} while(0)
|
||||
|
||||
#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
RUN_MEAN_STD1(type, (array), (rarray), (ss), (div), (isStd));\
|
||||
(array) -= (ss).strides[0] * (ss).shape[0];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
|
||||
(array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
|
||||
(array) -= (ss).strides[ULAB_MAX_DIMS - 2] * (ss).shape[ULAB_MAX_DIMS - 2];\
|
||||
(array) += (ss).strides[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (ss).shape[ULAB_MAX_DIMS - 3]);\
|
||||
} while(0)
|
||||
|
||||
#define RUN_ARGMIN(ndarray, type, array, results, rarray, shape, strides, index, op) do {\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
RUN_ARGMIN1((ndarray), type, (array), (results), (rarray), (index), (op));\
|
||||
(array) -= (ndarray)->strides[(index)] * (ndarray)->shape[(index)];\
|
||||
(array) += (strides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (shape)[ULAB_MAX_DIMS - 1]);\
|
||||
(array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
|
||||
(array) += (strides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (shape)[ULAB_MAX_DIMS - 2]);\
|
||||
(array) -= (strides)[ULAB_MAX_DIMS - 2] * (shape)[ULAB_MAX_DIMS-2];\
|
||||
(array) += (strides)[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (shape)[ULAB_MAX_DIMS - 3]);\
|
||||
} while(0)
|
||||
|
||||
#define RUN_DIFF(ndarray, type, array, results, rarray, shape, strides, index, stencil, N) do {\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
RUN_DIFF1((ndarray), type, (array), (results), (rarray), (index), (stencil), (N));\
|
||||
(array) -= (ndarray)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(array) += (ndarray)->strides[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(rarray) += (results)->strides[ULAB_MAX_DIMS - 2];\
|
||||
l++;\
|
||||
} while(l < (shape)[ULAB_MAX_DIMS - 2]);\
|
||||
(array) -= (strides)[ULAB_MAX_DIMS - 2] * (shape)[ULAB_MAX_DIMS-2];\
|
||||
(array) += (strides)[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(rarray) += (results)->strides[ULAB_MAX_DIMS - 3];\
|
||||
k++;\
|
||||
} while(k < (shape)[ULAB_MAX_DIMS - 3]);\
|
||||
(array) -= (strides)[ULAB_MAX_DIMS - 3] * (shape)[ULAB_MAX_DIMS-3];\
|
||||
(array) += (strides)[ULAB_MAX_DIMS - 4];\
|
||||
(rarray) -= (results)->strides[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(rarray) += (results)->strides[ULAB_MAX_DIMS - 4];\
|
||||
j++;\
|
||||
} while(j < (shape)[ULAB_MAX_DIMS - 4]);\
|
||||
} while(0)
|
||||
|
||||
#define HEAPSORT(ndarray, type, array, shape, strides, index, increment, N) do {\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
HEAPSORT1(type, (array), (increment), (N));\
|
||||
(array) += (strides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (shape)[ULAB_MAX_DIMS - 1]);\
|
||||
(array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
|
||||
(array) += (strides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (shape)[ULAB_MAX_DIMS - 2]);\
|
||||
(array) -= (strides)[ULAB_MAX_DIMS - 2] * (shape)[ULAB_MAX_DIMS-2];\
|
||||
(array) += (strides)[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (shape)[ULAB_MAX_DIMS - 3]);\
|
||||
} while(0)
|
||||
|
||||
#define HEAP_ARGSORT(ndarray, type, array, shape, strides, index, increment, N, iarray, istrides, iincrement) do {\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
HEAP_ARGSORT1(type, (array), (increment), (N), (iarray), (iincrement));\
|
||||
(array) += (strides)[ULAB_MAX_DIMS - 1];\
|
||||
(iarray) += (istrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (shape)[ULAB_MAX_DIMS - 1]);\
|
||||
(iarray) -= (istrides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
|
||||
(iarray) += (istrides)[ULAB_MAX_DIMS - 2];\
|
||||
(array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
|
||||
(array) += (strides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (shape)[ULAB_MAX_DIMS - 2]);\
|
||||
(iarray) -= (istrides)[ULAB_MAX_DIMS - 2] * (shape)[ULAB_MAX_DIMS-2];\
|
||||
(iarray) += (istrides)[ULAB_MAX_DIMS - 3];\
|
||||
(array) -= (strides)[ULAB_MAX_DIMS - 2] * (shape)[ULAB_MAX_DIMS-2];\
|
||||
(array) += (strides)[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (shape)[ULAB_MAX_DIMS - 3]);\
|
||||
} while(0)
|
||||
|
||||
#endif
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_all_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_any_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_argmax_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_argmin_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_argsort_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(numerical_cross_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_diff_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_flip_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_max_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_mean_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_median_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_min_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_roll_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_std_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_sum_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_sort_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(numerical_sort_inplace_obj);
|
||||
|
||||
#endif
|
||||
408
code/numpy/numpy.c
Normal file
408
code/numpy/numpy.c
Normal file
|
|
@ -0,0 +1,408 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
|
||||
* 2020 Scott Shawcroft for Adafruit Industries
|
||||
* 2020-2022 Zoltán Vörös
|
||||
* 2020 Taku Fukada
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include "py/runtime.h"
|
||||
|
||||
#include "numpy.h"
|
||||
#include "approx.h"
|
||||
#include "bitwise.h"
|
||||
#include "carray/carray.h"
|
||||
#include "compare.h"
|
||||
#include "create.h"
|
||||
#include "fft/fft.h"
|
||||
#include "filter.h"
|
||||
#include "io/io.h"
|
||||
#include "linalg/linalg.h"
|
||||
#include "numerical.h"
|
||||
#include "random/random.h"
|
||||
#include "stats.h"
|
||||
#include "transform.h"
|
||||
#include "poly.h"
|
||||
#include "vector.h"
|
||||
|
||||
//| """Compatibility layer for numpy"""
|
||||
//|
|
||||
|
||||
//| class ndarray: ...
|
||||
|
||||
//| def get_printoptions() -> Dict[str, int]:
|
||||
//| """Get printing options"""
|
||||
//| ...
|
||||
//|
|
||||
//| def set_printoptions(threshold: Optional[int] = None, edgeitems: Optional[int] = None) -> None:
|
||||
//| """Set printing options"""
|
||||
//| ...
|
||||
//|
|
||||
//| def ndinfo(array: ulab.numpy.ndarray) -> None:
|
||||
//| ...
|
||||
//|
|
||||
//| def array(
|
||||
//| values: Union[ndarray, Iterable[Union[_float, _bool, Iterable[Any]]]],
|
||||
//| *,
|
||||
//| dtype: _DType = ulab.numpy.float
|
||||
//| ) -> ulab.numpy.ndarray:
|
||||
//| """alternate constructor function for `ulab.numpy.ndarray`. Mirrors numpy.array"""
|
||||
//| ...
|
||||
|
||||
// math constants
|
||||
#if ULAB_NUMPY_HAS_E
|
||||
ULAB_DEFINE_FLOAT_CONST(ulab_const_float_e, MP_E, 0x402df854UL, 0x4005bf0a8b145769ULL);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_INF
|
||||
ULAB_DEFINE_FLOAT_CONST(numpy_const_float_inf, (mp_float_t)INFINITY, 0x7f800000UL, 0x7ff0000000000000ULL);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_NAN
|
||||
ULAB_DEFINE_FLOAT_CONST(numpy_const_float_nan, (mp_float_t)NAN, 0x7fc00000UL, 0x7ff8000000000000ULL);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_PI
|
||||
ULAB_DEFINE_FLOAT_CONST(ulab_const_float_pi, MP_PI, 0x40490fdbUL, 0x400921fb54442d18ULL);
|
||||
#endif
|
||||
|
||||
static const mp_rom_map_elem_t ulab_numpy_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_numpy) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ndarray), MP_ROM_PTR(&ulab_ndarray_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_array), MP_ROM_PTR(&ndarray_array_constructor_obj) },
|
||||
#if ULAB_NUMPY_HAS_FROMBUFFER
|
||||
{ MP_ROM_QSTR(MP_QSTR_frombuffer), MP_ROM_PTR(&create_frombuffer_obj) },
|
||||
#endif
|
||||
// math constants
|
||||
#if ULAB_NUMPY_HAS_E
|
||||
{ MP_ROM_QSTR(MP_QSTR_e), ULAB_REFERENCE_FLOAT_CONST(ulab_const_float_e) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_INF
|
||||
{ MP_ROM_QSTR(MP_QSTR_inf), ULAB_REFERENCE_FLOAT_CONST(numpy_const_float_inf) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_NAN
|
||||
{ MP_ROM_QSTR(MP_QSTR_nan), ULAB_REFERENCE_FLOAT_CONST(numpy_const_float_nan) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_PI
|
||||
{ MP_ROM_QSTR(MP_QSTR_pi), ULAB_REFERENCE_FLOAT_CONST(ulab_const_float_pi) },
|
||||
#endif
|
||||
// class constants, always included
|
||||
{ MP_ROM_QSTR(MP_QSTR_bool), MP_ROM_INT(NDARRAY_BOOL) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_uint8), MP_ROM_INT(NDARRAY_UINT8) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_int8), MP_ROM_INT(NDARRAY_INT8) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_uint16), MP_ROM_INT(NDARRAY_UINT16) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_int16), MP_ROM_INT(NDARRAY_INT16) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_float), MP_ROM_INT(NDARRAY_FLOAT) },
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
{ MP_ROM_QSTR(MP_QSTR_complex), MP_ROM_INT(NDARRAY_COMPLEX) },
|
||||
#endif
|
||||
// modules of numpy
|
||||
#if ULAB_NUMPY_HAS_FFT_MODULE
|
||||
{ MP_ROM_QSTR(MP_QSTR_fft), MP_ROM_PTR(&ulab_fft_module) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_LINALG_MODULE
|
||||
{ MP_ROM_QSTR(MP_QSTR_linalg), MP_ROM_PTR(&ulab_linalg_module) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_RANDOM_MODULE
|
||||
{ MP_ROM_QSTR(MP_QSTR_random), MP_ROM_PTR(&ulab_numpy_random_module) },
|
||||
#endif
|
||||
#if ULAB_HAS_PRINTOPTIONS
|
||||
{ MP_ROM_QSTR(MP_QSTR_set_printoptions), MP_ROM_PTR(&ndarray_set_printoptions_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_get_printoptions), MP_ROM_PTR(&ndarray_get_printoptions_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_NDINFO
|
||||
{ MP_ROM_QSTR(MP_QSTR_ndinfo), MP_ROM_PTR(&ndarray_info_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_ARANGE
|
||||
{ MP_ROM_QSTR(MP_QSTR_arange), MP_ROM_PTR(&create_arange_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_COMPRESS
|
||||
{ MP_ROM_QSTR(MP_QSTR_compress), MP_ROM_PTR(&transform_compress_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_CONCATENATE
|
||||
{ MP_ROM_QSTR(MP_QSTR_concatenate), MP_ROM_PTR(&create_concatenate_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_DELETE
|
||||
{ MP_ROM_QSTR(MP_QSTR_delete), MP_ROM_PTR(&transform_delete_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_DIAG
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
{ MP_ROM_QSTR(MP_QSTR_diag), MP_ROM_PTR(&create_diag_obj) },
|
||||
#endif
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_EMPTY
|
||||
{ MP_ROM_QSTR(MP_QSTR_empty), MP_ROM_PTR(&create_zeros_obj) },
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
#if ULAB_NUMPY_HAS_EYE
|
||||
{ MP_ROM_QSTR(MP_QSTR_eye), MP_ROM_PTR(&create_eye_obj) },
|
||||
#endif
|
||||
#endif /* ULAB_MAX_DIMS */
|
||||
// functions of the approx sub-module
|
||||
#if ULAB_NUMPY_HAS_INTERP
|
||||
{ MP_ROM_QSTR(MP_QSTR_interp), MP_ROM_PTR(&approx_interp_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_TRAPZ
|
||||
{ MP_ROM_QSTR(MP_QSTR_trapz), MP_ROM_PTR(&approx_trapz_obj) },
|
||||
#endif
|
||||
// functions of the create sub-module
|
||||
#if ULAB_NUMPY_HAS_FULL
|
||||
{ MP_ROM_QSTR(MP_QSTR_full), MP_ROM_PTR(&create_full_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_LINSPACE
|
||||
{ MP_ROM_QSTR(MP_QSTR_linspace), MP_ROM_PTR(&create_linspace_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_LOGSPACE
|
||||
{ MP_ROM_QSTR(MP_QSTR_logspace), MP_ROM_PTR(&create_logspace_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_ONES
|
||||
{ MP_ROM_QSTR(MP_QSTR_ones), MP_ROM_PTR(&create_ones_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_ZEROS
|
||||
{ MP_ROM_QSTR(MP_QSTR_zeros), MP_ROM_PTR(&create_zeros_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_CLIP
|
||||
{ MP_ROM_QSTR(MP_QSTR_clip), MP_ROM_PTR(&compare_clip_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_EQUAL
|
||||
{ MP_ROM_QSTR(MP_QSTR_equal), MP_ROM_PTR(&compare_equal_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_NOTEQUAL
|
||||
{ MP_ROM_QSTR(MP_QSTR_not_equal), MP_ROM_PTR(&compare_not_equal_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_ISFINITE
|
||||
{ MP_ROM_QSTR(MP_QSTR_isfinite), MP_ROM_PTR(&compare_isfinite_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_ISINF
|
||||
{ MP_ROM_QSTR(MP_QSTR_isinf), MP_ROM_PTR(&compare_isinf_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_MAXIMUM
|
||||
{ MP_ROM_QSTR(MP_QSTR_maximum), MP_ROM_PTR(&compare_maximum_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_MINIMUM
|
||||
{ MP_ROM_QSTR(MP_QSTR_minimum), MP_ROM_PTR(&compare_minimum_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_NONZERO
|
||||
{ MP_ROM_QSTR(MP_QSTR_nonzero), MP_ROM_PTR(&compare_nonzero_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_WHERE
|
||||
{ MP_ROM_QSTR(MP_QSTR_where), MP_ROM_PTR(&compare_where_obj) },
|
||||
#endif
|
||||
// bitwise operators
|
||||
#if ULAB_NUMPY_HAS_BITWISE_AND
|
||||
{ MP_ROM_QSTR(MP_QSTR_bitwise_and), MP_ROM_PTR(&bitwise_bitwise_and_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_BITWISE_OR
|
||||
{ MP_ROM_QSTR(MP_QSTR_bitwise_or), MP_ROM_PTR(&bitwise_bitwise_or_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_BITWISE_XOR
|
||||
{ MP_ROM_QSTR(MP_QSTR_bitwise_xor), MP_ROM_PTR(&bitwise_bitwise_xor_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_LEFT_SHIFT
|
||||
{ MP_ROM_QSTR(MP_QSTR_left_shift), MP_ROM_PTR(&left_shift_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_RIGHT_SHIFT
|
||||
{ MP_ROM_QSTR(MP_QSTR_right_shift), MP_ROM_PTR(&right_shift_obj) },
|
||||
#endif
|
||||
// functions of the filter sub-module
|
||||
#if ULAB_NUMPY_HAS_CONVOLVE
|
||||
{ MP_ROM_QSTR(MP_QSTR_convolve), MP_ROM_PTR(&filter_convolve_obj) },
|
||||
#endif
|
||||
// functions of the numerical sub-module
|
||||
#if ULAB_NUMPY_HAS_ALL
|
||||
{ MP_ROM_QSTR(MP_QSTR_all), MP_ROM_PTR(&numerical_all_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_ANY
|
||||
{ MP_ROM_QSTR(MP_QSTR_any), MP_ROM_PTR(&numerical_any_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_ARGMINMAX
|
||||
{ MP_ROM_QSTR(MP_QSTR_argmax), MP_ROM_PTR(&numerical_argmax_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_argmin), MP_ROM_PTR(&numerical_argmin_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_ARGSORT
|
||||
{ MP_ROM_QSTR(MP_QSTR_argsort), MP_ROM_PTR(&numerical_argsort_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_ASARRAY
|
||||
{ MP_ROM_QSTR(MP_QSTR_asarray), MP_ROM_PTR(&create_asarray_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_CROSS
|
||||
{ MP_ROM_QSTR(MP_QSTR_cross), MP_ROM_PTR(&numerical_cross_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_DIFF
|
||||
{ MP_ROM_QSTR(MP_QSTR_diff), MP_ROM_PTR(&numerical_diff_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_DOT
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
{ MP_ROM_QSTR(MP_QSTR_dot), MP_ROM_PTR(&transform_dot_obj) },
|
||||
#endif
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_TRACE
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
{ MP_ROM_QSTR(MP_QSTR_trace), MP_ROM_PTR(&stats_trace_obj) },
|
||||
#endif
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_FLIP
|
||||
{ MP_ROM_QSTR(MP_QSTR_flip), MP_ROM_PTR(&numerical_flip_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_LOAD
|
||||
{ MP_ROM_QSTR(MP_QSTR_load), MP_ROM_PTR(&io_load_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_LOADTXT
|
||||
{ MP_ROM_QSTR(MP_QSTR_loadtxt), MP_ROM_PTR(&io_loadtxt_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_MINMAX
|
||||
{ MP_ROM_QSTR(MP_QSTR_max), MP_ROM_PTR(&numerical_max_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_MEAN
|
||||
{ MP_ROM_QSTR(MP_QSTR_mean), MP_ROM_PTR(&numerical_mean_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_MEDIAN
|
||||
{ MP_ROM_QSTR(MP_QSTR_median), MP_ROM_PTR(&numerical_median_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_MINMAX
|
||||
{ MP_ROM_QSTR(MP_QSTR_min), MP_ROM_PTR(&numerical_min_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_ROLL
|
||||
{ MP_ROM_QSTR(MP_QSTR_roll), MP_ROM_PTR(&numerical_roll_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_SAVE
|
||||
{ MP_ROM_QSTR(MP_QSTR_save), MP_ROM_PTR(&io_save_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_SAVETXT
|
||||
{ MP_ROM_QSTR(MP_QSTR_savetxt), MP_ROM_PTR(&io_savetxt_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_SIZE
|
||||
{ MP_ROM_QSTR(MP_QSTR_size), MP_ROM_PTR(&transform_size_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_SORT
|
||||
{ MP_ROM_QSTR(MP_QSTR_sort), MP_ROM_PTR(&numerical_sort_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_STD
|
||||
{ MP_ROM_QSTR(MP_QSTR_std), MP_ROM_PTR(&numerical_std_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_SUM
|
||||
{ MP_ROM_QSTR(MP_QSTR_sum), MP_ROM_PTR(&numerical_sum_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_TAKE
|
||||
{ MP_ROM_QSTR(MP_QSTR_take), MP_ROM_PTR(&create_take_obj) },
|
||||
#endif
|
||||
// functions of the poly sub-module
|
||||
#if ULAB_NUMPY_HAS_POLYFIT
|
||||
{ MP_ROM_QSTR(MP_QSTR_polyfit), MP_ROM_PTR(&poly_polyfit_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_POLYVAL
|
||||
{ MP_ROM_QSTR(MP_QSTR_polyval), MP_ROM_PTR(&poly_polyval_obj) },
|
||||
#endif
|
||||
// functions of the vector sub-module
|
||||
#if ULAB_NUMPY_HAS_ACOS
|
||||
{ MP_ROM_QSTR(MP_QSTR_acos), MP_ROM_PTR(&vector_acos_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_ACOSH
|
||||
{ MP_ROM_QSTR(MP_QSTR_acosh), MP_ROM_PTR(&vector_acosh_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_ARCTAN2
|
||||
{ MP_ROM_QSTR(MP_QSTR_arctan2), MP_ROM_PTR(&vector_arctan2_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_AROUND
|
||||
{ MP_ROM_QSTR(MP_QSTR_around), MP_ROM_PTR(&vector_around_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_ASIN
|
||||
{ MP_ROM_QSTR(MP_QSTR_asin), MP_ROM_PTR(&vector_asin_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_ASINH
|
||||
{ MP_ROM_QSTR(MP_QSTR_asinh), MP_ROM_PTR(&vector_asinh_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_ATAN
|
||||
{ MP_ROM_QSTR(MP_QSTR_atan), MP_ROM_PTR(&vector_atan_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_ATANH
|
||||
{ MP_ROM_QSTR(MP_QSTR_atanh), MP_ROM_PTR(&vector_atanh_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_CEIL
|
||||
{ MP_ROM_QSTR(MP_QSTR_ceil), MP_ROM_PTR(&vector_ceil_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_COS
|
||||
{ MP_ROM_QSTR(MP_QSTR_cos), MP_ROM_PTR(&vector_cos_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_COSH
|
||||
{ MP_ROM_QSTR(MP_QSTR_cosh), MP_ROM_PTR(&vector_cosh_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_DEGREES
|
||||
{ MP_ROM_QSTR(MP_QSTR_degrees), MP_ROM_PTR(&vector_degrees_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_EXP
|
||||
{ MP_ROM_QSTR(MP_QSTR_exp), MP_ROM_PTR(&vector_exp_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_EXPM1
|
||||
{ MP_ROM_QSTR(MP_QSTR_expm1), MP_ROM_PTR(&vector_expm1_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_FLOOR
|
||||
{ MP_ROM_QSTR(MP_QSTR_floor), MP_ROM_PTR(&vector_floor_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_LOG
|
||||
{ MP_ROM_QSTR(MP_QSTR_log), MP_ROM_PTR(&vector_log_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_LOG10
|
||||
{ MP_ROM_QSTR(MP_QSTR_log10), MP_ROM_PTR(&vector_log10_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_LOG2
|
||||
{ MP_ROM_QSTR(MP_QSTR_log2), MP_ROM_PTR(&vector_log2_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_RADIANS
|
||||
{ MP_ROM_QSTR(MP_QSTR_radians), MP_ROM_PTR(&vector_radians_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_SIN
|
||||
{ MP_ROM_QSTR(MP_QSTR_sin), MP_ROM_PTR(&vector_sin_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_SINC
|
||||
{ MP_ROM_QSTR(MP_QSTR_sinc), MP_ROM_PTR(&vector_sinc_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_SINH
|
||||
{ MP_ROM_QSTR(MP_QSTR_sinh), MP_ROM_PTR(&vector_sinh_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_SQRT
|
||||
{ MP_ROM_QSTR(MP_QSTR_sqrt), MP_ROM_PTR(&vector_sqrt_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_TAN
|
||||
{ MP_ROM_QSTR(MP_QSTR_tan), MP_ROM_PTR(&vector_tan_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_TANH
|
||||
{ MP_ROM_QSTR(MP_QSTR_tanh), MP_ROM_PTR(&vector_tanh_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_VECTORIZE
|
||||
{ MP_ROM_QSTR(MP_QSTR_vectorize), MP_ROM_PTR(&vector_vectorize_obj) },
|
||||
#endif
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
#if ULAB_NUMPY_HAS_REAL
|
||||
{ MP_ROM_QSTR(MP_QSTR_real), MP_ROM_PTR(&carray_real_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_IMAG
|
||||
{ MP_ROM_QSTR(MP_QSTR_imag), MP_ROM_PTR(&carray_imag_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_CONJUGATE
|
||||
{ MP_ROM_QSTR(MP_QSTR_conjugate), MP_ROM_PTR(&carray_conjugate_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_HAS_SORT_COMPLEX
|
||||
{ MP_ROM_QSTR(MP_QSTR_sort_complex), MP_ROM_PTR(&carray_sort_complex_obj) },
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT(mp_module_ulab_numpy_globals, ulab_numpy_globals_table);
|
||||
|
||||
const mp_obj_module_t ulab_numpy_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_numpy_globals,
|
||||
};
|
||||
|
||||
#if CIRCUITPY_ULAB
|
||||
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy, ulab_numpy_module);
|
||||
#endif
|
||||
21
code/numpy/numpy.h
Normal file
21
code/numpy/numpy.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2021 Zoltán Vörös
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _NUMPY_
|
||||
#define _NUMPY_
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "../ndarray.h"
|
||||
|
||||
extern const mp_obj_module_t ulab_numpy_module;
|
||||
|
||||
#endif /* _NUMPY_ */
|
||||
218
code/numpy/poly.c
Normal file
218
code/numpy/poly.c
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2021 Zoltán Vörös
|
||||
* 2020 Jeff Epler for Adafruit Industries
|
||||
* 2020 Scott Shawcroft for Adafruit Industries
|
||||
* 2020 Taku Fukada
|
||||
*/
|
||||
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/objarray.h"
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "linalg/linalg_tools.h"
|
||||
#include "../ulab_tools.h"
|
||||
#include "carray/carray_tools.h"
|
||||
#include "poly.h"
|
||||
|
||||
#if ULAB_NUMPY_HAS_POLYFIT
|
||||
|
||||
mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) {
|
||||
if(!ndarray_object_is_array_like(args[0])) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("input data must be an iterable"));
|
||||
}
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
if(mp_obj_is_type(args[0], &ulab_ndarray_type)) {
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0]);
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
|
||||
}
|
||||
#endif
|
||||
size_t lenx = 0, leny = 0;
|
||||
uint8_t deg = 0;
|
||||
mp_float_t *x, *XT, *y, *prod;
|
||||
|
||||
if(n_args == 2) { // only the y values are supplied
|
||||
// TODO: this is actually not enough: the first argument can very well be a matrix,
|
||||
// in which case we are between the rock and a hard place
|
||||
leny = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[0]));
|
||||
deg = (uint8_t)mp_obj_get_int(args[1]);
|
||||
if(leny < deg) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("more degrees of freedom than data points"));
|
||||
}
|
||||
lenx = leny;
|
||||
x = m_new(mp_float_t, lenx); // assume uniformly spaced data points
|
||||
for(size_t i=0; i < lenx; i++) {
|
||||
x[i] = i;
|
||||
}
|
||||
y = m_new(mp_float_t, leny);
|
||||
fill_array_iterable(y, args[0]);
|
||||
} else /* n_args == 3 */ {
|
||||
if(!ndarray_object_is_array_like(args[1])) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("input data must be an iterable"));
|
||||
}
|
||||
lenx = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[0]));
|
||||
leny = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[1]));
|
||||
if(lenx != leny) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("input vectors must be of equal length"));
|
||||
}
|
||||
deg = (uint8_t)mp_obj_get_int(args[2]);
|
||||
if(leny < deg) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("more degrees of freedom than data points"));
|
||||
}
|
||||
x = m_new(mp_float_t, lenx);
|
||||
fill_array_iterable(x, args[0]);
|
||||
y = m_new(mp_float_t, leny);
|
||||
fill_array_iterable(y, args[1]);
|
||||
}
|
||||
|
||||
// one could probably express X as a function of XT,
|
||||
// and thereby save RAM, because X is used only in the product
|
||||
XT = m_new(mp_float_t, (deg+1)*leny); // XT is a matrix of shape (deg+1, len) (rows, columns)
|
||||
for(size_t i=0; i < leny; i++) { // column index
|
||||
XT[i+0*lenx] = 1.0; // top row
|
||||
for(uint8_t j=1; j < deg+1; j++) { // row index
|
||||
XT[i+j*leny] = XT[i+(j-1)*leny]*x[i];
|
||||
}
|
||||
}
|
||||
|
||||
prod = m_new(mp_float_t, (deg+1)*(deg+1)); // the product matrix is of shape (deg+1, deg+1)
|
||||
mp_float_t sum;
|
||||
for(uint8_t i=0; i < deg+1; i++) { // column index
|
||||
for(uint8_t j=0; j < deg+1; j++) { // row index
|
||||
sum = 0.0;
|
||||
for(size_t k=0; k < lenx; k++) {
|
||||
// (j, k) * (k, i)
|
||||
// Note that the second matrix is simply the transpose of the first:
|
||||
// X(k, i) = XT(i, k) = XT[k*lenx+i]
|
||||
sum += XT[j*lenx+k]*XT[i*lenx+k]; // X[k*(deg+1)+i];
|
||||
}
|
||||
prod[j*(deg+1)+i] = sum;
|
||||
}
|
||||
}
|
||||
if(!linalg_invert_matrix(prod, deg+1)) {
|
||||
// Although X was a Vandermonde matrix, whose inverse is guaranteed to exist,
|
||||
// we bail out here, if prod couldn't be inverted: if the values in x are not all
|
||||
// distinct, prod is singular
|
||||
m_del(mp_float_t, XT, (deg+1)*lenx);
|
||||
m_del(mp_float_t, x, lenx);
|
||||
m_del(mp_float_t, y, lenx);
|
||||
m_del(mp_float_t, prod, (deg+1)*(deg+1));
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("could not invert Vandermonde matrix"));
|
||||
}
|
||||
// at this point, we have the inverse of X^T * X
|
||||
// y is a column vector; x is free now, we can use it for storing intermediate values
|
||||
for(uint8_t i=0; i < deg+1; i++) { // row index
|
||||
sum = 0.0;
|
||||
for(size_t j=0; j < lenx; j++) { // column index
|
||||
sum += XT[i*lenx+j]*y[j];
|
||||
}
|
||||
x[i] = sum;
|
||||
}
|
||||
// XT is no longer needed
|
||||
m_del(mp_float_t, XT, (deg+1)*leny);
|
||||
|
||||
ndarray_obj_t *beta = ndarray_new_linear_array(deg+1, NDARRAY_FLOAT);
|
||||
mp_float_t *betav = (mp_float_t *)beta->array;
|
||||
// x[0..(deg+1)] contains now the product X^T * y; we can get rid of y
|
||||
m_del(mp_float_t, y, leny);
|
||||
|
||||
// now, we calculate beta, i.e., we apply prod = (X^T * X)^(-1) on x = X^T * y; x is a column vector now
|
||||
for(uint8_t i=0; i < deg+1; i++) {
|
||||
sum = 0.0;
|
||||
for(uint8_t j=0; j < deg+1; j++) {
|
||||
sum += prod[i*(deg+1)+j]*x[j];
|
||||
}
|
||||
betav[i] = sum;
|
||||
}
|
||||
m_del(mp_float_t, x, lenx);
|
||||
m_del(mp_float_t, prod, (deg+1)*(deg+1));
|
||||
for(uint8_t i=0; i < (deg+1)/2; i++) {
|
||||
// We have to reverse the array, for the leading coefficient comes first.
|
||||
SWAP(mp_float_t, betav[i], betav[deg-i]);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(beta);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poly_polyfit_obj, 2, 3, poly_polyfit);
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_HAS_POLYVAL
|
||||
|
||||
static mp_float_t poly_eval(mp_float_t x, mp_float_t *p, uint8_t plen) {
|
||||
mp_float_t y = p[0];
|
||||
for(uint8_t j=0; j < plen-1; j++) {
|
||||
y *= x;
|
||||
y += p[j+1];
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) {
|
||||
if(!ndarray_object_is_array_like(o_p)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("input is not iterable"));
|
||||
}
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
ndarray_obj_t *input;
|
||||
if(mp_obj_is_type(o_p, &ulab_ndarray_type)) {
|
||||
input = MP_OBJ_TO_PTR(o_p);
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(input->dtype)
|
||||
}
|
||||
if(mp_obj_is_type(o_x, &ulab_ndarray_type)) {
|
||||
input = MP_OBJ_TO_PTR(o_x);
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(input->dtype)
|
||||
}
|
||||
#endif
|
||||
// p had better be a one-dimensional standard iterable
|
||||
size_t plen = (size_t)mp_obj_get_int(mp_obj_len_maybe(o_p));
|
||||
mp_float_t *p = m_new(mp_float_t, plen);
|
||||
mp_obj_iter_buf_t p_buf;
|
||||
mp_obj_t p_item, p_iterable = mp_getiter(o_p, &p_buf);
|
||||
uint8_t i = 0;
|
||||
while((p_item = mp_iternext(p_iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
p[i] = mp_obj_get_float(p_item);
|
||||
i++;
|
||||
}
|
||||
|
||||
if(!ndarray_object_is_array_like(o_x)) {
|
||||
return mp_obj_new_float(poly_eval(mp_obj_get_float(o_x), p, plen));
|
||||
}
|
||||
|
||||
// polynomials are going to be of type float, except, when both
|
||||
// the coefficients and the independent variable are integers
|
||||
ndarray_obj_t *ndarray;
|
||||
if(mp_obj_is_type(o_x, &ulab_ndarray_type)) {
|
||||
ndarray_obj_t *source = MP_OBJ_TO_PTR(o_x);
|
||||
uint8_t *sarray = (uint8_t *)source->array;
|
||||
ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT);
|
||||
mp_float_t *array = (mp_float_t *)ndarray->array;
|
||||
|
||||
mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype);
|
||||
|
||||
// TODO: these loops are really nothing, but the re-impplementation of
|
||||
// ITERATE_VECTOR from vectorise.c. We could pass a function pointer here
|
||||
ITERATOR_HEAD();
|
||||
*array++ = poly_eval(func(sarray), p, plen);
|
||||
ITERATOR_TAIL(source, sarray);
|
||||
} else {
|
||||
// o_x had better be a one-dimensional standard iterable
|
||||
ndarray = ndarray_new_linear_array(mp_obj_get_int(mp_obj_len_maybe(o_x)), NDARRAY_FLOAT);
|
||||
mp_float_t *array = (mp_float_t *)ndarray->array;
|
||||
mp_obj_iter_buf_t x_buf;
|
||||
mp_obj_t x_item, x_iterable = mp_getiter(o_x, &x_buf);
|
||||
while ((x_item = mp_iternext(x_iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
*array++ = poly_eval(mp_obj_get_float(x_item), p, plen);
|
||||
}
|
||||
}
|
||||
m_del(mp_float_t, p, plen);
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(poly_polyval_obj, poly_polyval);
|
||||
#endif
|
||||
21
code/numpy/poly.h
Normal file
21
code/numpy/poly.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2021 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _POLY_
|
||||
#define _POLY_
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "../ndarray.h"
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(poly_polyfit_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(poly_polyval_obj);
|
||||
|
||||
#endif
|
||||
361
code/numpy/random/random.c
Normal file
361
code/numpy/random/random.c
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2024 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "py/builtin.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
|
||||
#include "random.h"
|
||||
|
||||
ULAB_DEFINE_FLOAT_CONST(random_zero, MICROPY_FLOAT_CONST(0.0), 0UL, 0ULL);
|
||||
ULAB_DEFINE_FLOAT_CONST(random_one, MICROPY_FLOAT_CONST(1.0), 0x3f800000UL, 0x3ff0000000000000ULL);
|
||||
|
||||
// methods of the Generator object
|
||||
static const mp_rom_map_elem_t random_generator_locals_dict_table[] = {
|
||||
#if ULAB_NUMPY_RANDOM_HAS_NORMAL
|
||||
{ MP_ROM_QSTR(MP_QSTR_normal), MP_ROM_PTR(&random_normal_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_RANDOM_HAS_RANDOM
|
||||
{ MP_ROM_QSTR(MP_QSTR_random), MP_ROM_PTR(&random_random_obj) },
|
||||
#endif
|
||||
#if ULAB_NUMPY_RANDOM_HAS_UNIFORM
|
||||
{ MP_ROM_QSTR(MP_QSTR_uniform), MP_ROM_PTR(&random_uniform_obj) },
|
||||
#endif
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT(random_generator_locals_dict, random_generator_locals_dict_table);
|
||||
|
||||
// random's Generator object is defined here
|
||||
#if defined(MP_DEFINE_CONST_OBJ_TYPE)
|
||||
MP_DEFINE_CONST_OBJ_TYPE(
|
||||
random_generator_type,
|
||||
MP_QSTR_generator,
|
||||
MP_TYPE_FLAG_NONE,
|
||||
print, random_generator_print,
|
||||
make_new, random_generator_make_new,
|
||||
locals_dict, &random_generator_locals_dict
|
||||
);
|
||||
#else
|
||||
const mp_obj_type_t random_generator_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_generator,
|
||||
.print = random_generator_print,
|
||||
.make_new = random_generator_make_new,
|
||||
.locals_dict = (mp_obj_dict_t*)&random_generator_locals_dict
|
||||
};
|
||||
#endif
|
||||
|
||||
void random_generator_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
(void)kind;
|
||||
random_generator_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_printf(MP_PYTHON_PRINTER, "Generator() at 0x%p", self);
|
||||
}
|
||||
|
||||
mp_obj_t random_generator_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
(void) type;
|
||||
mp_arg_check_num(n_args, n_kw, 0, 1, true);
|
||||
mp_map_t kw_args;
|
||||
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
|
||||
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
};
|
||||
mp_arg_val_t _args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, _args);
|
||||
|
||||
|
||||
if(args[0] == mp_const_none) {
|
||||
#ifndef MICROPY_PY_RANDOM_SEED_INIT_FUNC
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("no default seed"));
|
||||
#else
|
||||
random_generator_obj_t *generator = m_new_obj(random_generator_obj_t);
|
||||
generator->base.type = &random_generator_type;
|
||||
generator->state = MICROPY_PY_RANDOM_SEED_INIT_FUNC;
|
||||
return MP_OBJ_FROM_PTR(generator);
|
||||
#endif
|
||||
} else if(mp_obj_is_int(args[0])) {
|
||||
random_generator_obj_t *generator = m_new_obj(random_generator_obj_t);
|
||||
generator->base.type = &random_generator_type;
|
||||
generator->state = (size_t)mp_obj_get_int(args[0]);
|
||||
return MP_OBJ_FROM_PTR(generator);
|
||||
} else if(mp_obj_is_type(args[0], &mp_type_tuple)){
|
||||
mp_obj_tuple_t *seeds = MP_OBJ_TO_PTR(args[0]);
|
||||
mp_obj_t *items = m_new(mp_obj_t, seeds->len);
|
||||
|
||||
for(uint8_t i = 0; i < seeds->len; i++) {
|
||||
random_generator_obj_t *generator = m_new_obj(random_generator_obj_t);
|
||||
generator->base.type = &random_generator_type;
|
||||
generator->state = (size_t)mp_obj_get_int(seeds->items[i]);
|
||||
items[i] = generator;
|
||||
}
|
||||
return mp_obj_new_tuple(seeds->len, items);
|
||||
} else {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("argument must be None, an integer or a tuple of integers"));
|
||||
}
|
||||
// we should never end up here
|
||||
return mp_const_none;
|
||||
}
|
||||
// END OF GENERATOR COMPONENTS
|
||||
|
||||
|
||||
static inline uint32_t pcg32_next(uint64_t *state) {
|
||||
uint64_t old_state = *state;
|
||||
*state = old_state * PCG_MULTIPLIER_64 + PCG_INCREMENT_64;
|
||||
uint32_t value = (uint32_t)((old_state ^ (old_state >> 18)) >> 27);
|
||||
int rot = old_state >> 59;
|
||||
return rot ? (value >> rot) | (value << (32 - rot)) : value;
|
||||
}
|
||||
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
|
||||
static inline uint64_t pcg32_next64(uint64_t *state) {
|
||||
uint64_t value = pcg32_next(state);
|
||||
value <<= 32;
|
||||
value |= pcg32_next(state);
|
||||
return value;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ULAB_NUMPY_RANDOM_HAS_NORMAL
|
||||
static mp_obj_t random_normal(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_loc, MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_zero) } },
|
||||
{ MP_QSTR_scale, MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_one) } },
|
||||
{ MP_QSTR_size, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
random_generator_obj_t *self = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
mp_float_t loc = mp_obj_get_float(args[1].u_obj);
|
||||
mp_float_t scale = mp_obj_get_float(args[2].u_obj);
|
||||
mp_obj_t size = args[3].u_obj;
|
||||
|
||||
ndarray_obj_t *ndarray = NULL;
|
||||
mp_float_t u, v, value;
|
||||
|
||||
if(size != mp_const_none) {
|
||||
if(mp_obj_is_int(size)) {
|
||||
ndarray = ndarray_new_linear_array((size_t)mp_obj_get_int(size), NDARRAY_FLOAT);
|
||||
} else if(mp_obj_is_type(size, &mp_type_tuple)) {
|
||||
mp_obj_tuple_t *_shape = MP_OBJ_TO_PTR(size);
|
||||
ndarray = ndarray_new_ndarray_from_tuple(_shape, NDARRAY_FLOAT);
|
||||
} else { // input type not supported
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("shape must be None, an integer or a tuple of integers"));
|
||||
}
|
||||
} else {
|
||||
// return single value
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
uint32_t x = pcg32_next(&self->state);
|
||||
u = (float)(int32_t)(x >> 8) * 0x1.0p-24f;
|
||||
x = pcg32_next(&self->state);
|
||||
v = (float)(int32_t)(x >> 8) * 0x1.0p-24f;
|
||||
#else
|
||||
uint64_t x = pcg32_next64(&self->state);
|
||||
u = (double)(int64_t)(x >> 11) * 0x1.0p-53;
|
||||
x = pcg32_next64(&self->state);
|
||||
v = (double)(int64_t)(x >> 11) * 0x1.0p-53;
|
||||
#endif
|
||||
mp_float_t sqrt_log = MICROPY_FLOAT_C_FUN(sqrt)(-MICROPY_FLOAT_CONST(2.0) * MICROPY_FLOAT_C_FUN(log)(u));
|
||||
value = sqrt_log * MICROPY_FLOAT_C_FUN(cos)(MICROPY_FLOAT_CONST(2.0) * MP_PI * v);
|
||||
return mp_obj_new_float(loc + scale * value);
|
||||
}
|
||||
|
||||
mp_float_t *array = (mp_float_t *)ndarray->array;
|
||||
|
||||
// numpy's random supports only dense output arrays, so we can simply
|
||||
// loop through the elements in a linear fashion
|
||||
for(size_t i = 0; i < ndarray->len; i = i + 2) {
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
uint32_t x = pcg32_next(&self->state);
|
||||
u = (float)(int32_t)(x >> 8) * 0x1.0p-24f;
|
||||
x = pcg32_next(&self->state);
|
||||
v = (float)(int32_t)(x >> 8) * 0x1.0p-24f;
|
||||
#else
|
||||
uint64_t x = pcg32_next64(&self->state);
|
||||
u = (double)(int64_t)(x >> 11) * 0x1.0p-53;
|
||||
x = pcg32_next64(&self->state);
|
||||
v = (double)(int64_t)(x >> 11) * 0x1.0p-53;
|
||||
#endif
|
||||
mp_float_t sqrt_log = MICROPY_FLOAT_C_FUN(sqrt)(-MICROPY_FLOAT_CONST(2.0) * MICROPY_FLOAT_C_FUN(log)(u));
|
||||
value = sqrt_log * MICROPY_FLOAT_C_FUN(cos)(MICROPY_FLOAT_CONST(2.0) * MP_PI * v);
|
||||
*array++ = loc + scale * value;
|
||||
if((i & 1) == 0) {
|
||||
value = sqrt_log * MICROPY_FLOAT_C_FUN(sin)(MICROPY_FLOAT_CONST(2.0) * MP_PI * v);
|
||||
*array++ = loc + scale * value;
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(random_normal_obj, 1, random_normal);
|
||||
#endif /* ULAB_NUMPY_RANDOM_HAS_NORMAL */
|
||||
|
||||
#if ULAB_NUMPY_RANDOM_HAS_RANDOM
|
||||
static mp_obj_t random_random(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_size, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
random_generator_obj_t *self = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
|
||||
mp_obj_t size = args[1].u_obj;
|
||||
mp_obj_t out = args[2].u_obj;
|
||||
|
||||
ndarray_obj_t *ndarray = NULL;
|
||||
size_t *shape = m_new0(size_t, ULAB_MAX_DIMS);
|
||||
|
||||
if(size != mp_const_none) {
|
||||
if(mp_obj_is_int(size)) {
|
||||
shape[ULAB_MAX_DIMS - 1] = (size_t)mp_obj_get_int(size);
|
||||
} else if(mp_obj_is_type(size, &mp_type_tuple)) {
|
||||
mp_obj_tuple_t *_shape = MP_OBJ_TO_PTR(size);
|
||||
ndarray = ndarray_new_ndarray_from_tuple(_shape, NDARRAY_FLOAT);
|
||||
} else { // input type not supported
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("shape must be None, an integer or a tuple of integers"));
|
||||
}
|
||||
}
|
||||
|
||||
if(out != mp_const_none) {
|
||||
if(!mp_obj_is_type(out, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("out has wrong type"));
|
||||
}
|
||||
|
||||
ndarray = MP_OBJ_TO_PTR(out);
|
||||
|
||||
if(ndarray->dtype != NDARRAY_FLOAT) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("output array has wrong type"));
|
||||
}
|
||||
if(size != mp_const_none) {
|
||||
for(uint8_t i = 0; i < ULAB_MAX_DIMS; i++) {
|
||||
if(ndarray->shape[i] != shape[i]) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("size must match out.shape when used together"));
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!ndarray_is_dense(ndarray)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("output array must be contiguous"));
|
||||
}
|
||||
} else { // out == None
|
||||
if(size != mp_const_none) {
|
||||
mp_obj_tuple_t *_shape = MP_OBJ_TO_PTR(size);
|
||||
ndarray = ndarray_new_ndarray_from_tuple(_shape, NDARRAY_FLOAT);
|
||||
} else {
|
||||
// return single value
|
||||
mp_float_t value;
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
uint32_t x = pcg32_next(&self->state);
|
||||
value = (float)(int32_t)(x >> 8) * 0x1.0p-24f;
|
||||
#else
|
||||
uint64_t x = pcg32_next64(&self->state);
|
||||
value = (double)(int64_t)(x >> 11) * 0x1.0p-53;
|
||||
#endif
|
||||
return mp_obj_new_float(value);
|
||||
}
|
||||
}
|
||||
|
||||
mp_float_t *array = (mp_float_t *)ndarray->array;
|
||||
|
||||
// numpy's random supports only dense output arrays, so we can simply
|
||||
// loop through the elements in a linear fashion
|
||||
for(size_t i = 0; i < ndarray->len; i++) {
|
||||
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
uint32_t x = pcg32_next(&self->state);
|
||||
*array = (float)(int32_t)(x >> 8) * 0x1.0p-24f;
|
||||
#else
|
||||
uint64_t x = pcg32_next64(&self->state);
|
||||
*array = (double)(int64_t)(x >> 11) * 0x1.0p-53;
|
||||
#endif
|
||||
|
||||
array++;
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(random_random_obj, 1, random_random);
|
||||
#endif /* ULAB_NUMPY_RANDOM_HAS_RANDOM */
|
||||
|
||||
#if ULAB_NUMPY_RANDOM_HAS_UNIFORM
|
||||
static mp_obj_t random_uniform(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_low, MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_zero) } },
|
||||
{ MP_QSTR_high, MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_one) } },
|
||||
{ MP_QSTR_size, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
random_generator_obj_t *self = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
mp_float_t low = mp_obj_get_float(args[1].u_obj);
|
||||
mp_float_t high = mp_obj_get_float(args[2].u_obj);
|
||||
mp_obj_t size = args[3].u_obj;
|
||||
|
||||
ndarray_obj_t *ndarray = NULL;
|
||||
|
||||
if(size == mp_const_none) {
|
||||
// return single value
|
||||
mp_float_t value;
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
uint32_t x = pcg32_next(&self->state);
|
||||
value = (float)(int32_t)(x >> 8) * 0x1.0p-24f;
|
||||
#else
|
||||
uint64_t x = pcg32_next64(&self->state);
|
||||
value = (double)(int64_t)(x >> 11) * 0x1.0p-53;
|
||||
#endif
|
||||
return mp_obj_new_float(value);
|
||||
} else if(mp_obj_is_type(size, &mp_type_tuple)) {
|
||||
mp_obj_tuple_t *_shape = MP_OBJ_TO_PTR(size);
|
||||
ndarray = ndarray_new_ndarray_from_tuple(_shape, NDARRAY_FLOAT);
|
||||
} else { // input type not supported
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("shape must be None, an integer or a tuple of integers"));
|
||||
}
|
||||
|
||||
mp_float_t *array = (mp_float_t *)ndarray->array;
|
||||
mp_float_t diff = high - low;
|
||||
for(size_t i = 0; i < ndarray->len; i++) {
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
uint32_t x = pcg32_next(&self->state);
|
||||
*array = (float)(int32_t)(x >> 8) * 0x1.0p-24f;
|
||||
#else
|
||||
uint64_t x = pcg32_next64(&self->state);
|
||||
*array = (double)(int64_t)(x >> 11) * 0x1.0p-53;
|
||||
#endif
|
||||
*array = low + diff * *array;
|
||||
array++;
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(random_uniform_obj, 1, random_uniform);
|
||||
#endif /* ULAB_NUMPY_RANDOM_HAS_UNIFORM */
|
||||
|
||||
|
||||
static const mp_rom_map_elem_t ulab_numpy_random_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_random) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_Generator), MP_ROM_PTR(&random_generator_type) },
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT(mp_module_ulab_numpy_random_globals, ulab_numpy_random_globals_table);
|
||||
|
||||
const mp_obj_module_t ulab_numpy_random_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_numpy_random_globals,
|
||||
};
|
||||
37
code/numpy/random/random.h
Normal file
37
code/numpy/random/random.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2024 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#include "../../ndarray.h"
|
||||
|
||||
#ifndef _NUMPY_RANDOM_
|
||||
#define _NUMPY_RANDOM_
|
||||
|
||||
|
||||
#define PCG_MULTIPLIER_64 6364136223846793005ULL
|
||||
#define PCG_INCREMENT_64 1442695040888963407ULL
|
||||
|
||||
extern const mp_obj_module_t ulab_numpy_random_module;
|
||||
|
||||
extern const mp_obj_type_t random_generator_type;
|
||||
|
||||
typedef struct _random_generator_obj_t {
|
||||
mp_obj_base_t base;
|
||||
uint64_t state;
|
||||
} random_generator_obj_t;
|
||||
|
||||
mp_obj_t random_generator_make_new(const mp_obj_type_t *, size_t , size_t , const mp_obj_t *);
|
||||
void random_generator_print(const mp_print_t *, mp_obj_t , mp_print_kind_t );
|
||||
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(random_normal_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(random_random_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(random_uniform_obj);
|
||||
|
||||
#endif
|
||||
54
code/numpy/stats.c
Normal file
54
code/numpy/stats.c
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2021 Zoltán Vörös
|
||||
* 2020 Scott Shawcroft for Adafruit Industries
|
||||
* 2020 Roberto Colistete Jr.
|
||||
* 2020 Taku Fukada
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "../ulab_tools.h"
|
||||
#include "carray/carray_tools.h"
|
||||
#include "stats.h"
|
||||
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
#if ULAB_NUMPY_HAS_TRACE
|
||||
|
||||
//| def trace(m: ulab.numpy.ndarray) -> _float:
|
||||
//| """
|
||||
//| :param m: a square matrix
|
||||
//|
|
||||
//| Compute the trace of the matrix, the sum of its diagonal elements."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t stats_trace(mp_obj_t oin) {
|
||||
ndarray_obj_t *ndarray = tools_object_is_square(oin);
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
|
||||
mp_float_t trace = 0.0;
|
||||
for(size_t i=0; i < ndarray->shape[ULAB_MAX_DIMS - 1]; i++) {
|
||||
int32_t pos = i * (ndarray->strides[ULAB_MAX_DIMS - 1] + ndarray->strides[ULAB_MAX_DIMS - 2]);
|
||||
trace += ndarray_get_float_index(ndarray->array, ndarray->dtype, pos/ndarray->itemsize);
|
||||
}
|
||||
if(ndarray->dtype == NDARRAY_FLOAT) {
|
||||
return mp_obj_new_float(trace);
|
||||
}
|
||||
return mp_obj_new_int_from_float(trace);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(stats_trace_obj, stats_trace);
|
||||
#endif
|
||||
#endif
|
||||
20
code/numpy/stats.h
Normal file
20
code/numpy/stats.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2021 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _STATS_
|
||||
#define _STATS_
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "../ndarray.h"
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(stats_trace_obj);
|
||||
|
||||
#endif
|
||||
456
code/numpy/transform.c
Normal file
456
code/numpy/transform.c
Normal file
|
|
@ -0,0 +1,456 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2021 Zoltán Vörös
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "../ulab_tools.h"
|
||||
#include "carray/carray_tools.h"
|
||||
#include "numerical.h"
|
||||
#include "transform.h"
|
||||
|
||||
#if ULAB_NUMPY_HAS_COMPRESS
|
||||
static mp_obj_t transform_compress(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
mp_obj_t condition = args[0].u_obj;
|
||||
|
||||
if(!mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("wrong input type"));
|
||||
}
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[1].u_obj);
|
||||
uint8_t *array = (uint8_t *)ndarray->array;
|
||||
|
||||
mp_obj_t axis = args[2].u_obj;
|
||||
|
||||
size_t len = MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(condition));
|
||||
int8_t ax, shift_ax = 0;
|
||||
|
||||
if(axis != mp_const_none) {
|
||||
ax = tools_get_axis(axis, ndarray->ndim);
|
||||
shift_ax = ULAB_MAX_DIMS - ndarray->ndim + ax;
|
||||
}
|
||||
|
||||
if(((axis == mp_const_none) && (len != ndarray->len)) ||
|
||||
((axis != mp_const_none) && (len != ndarray->shape[shift_ax]))) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("wrong length of condition array"));
|
||||
}
|
||||
|
||||
size_t true_count = 0;
|
||||
mp_obj_iter_buf_t iter_buf;
|
||||
mp_obj_t item, iterable = mp_getiter(condition, &iter_buf);
|
||||
while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
if(mp_obj_is_true(item)) {
|
||||
true_count++;
|
||||
}
|
||||
}
|
||||
|
||||
iterable = mp_getiter(condition, &iter_buf);
|
||||
|
||||
ndarray_obj_t *result = NULL;
|
||||
|
||||
size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
|
||||
memcpy(shape, ndarray->shape, ULAB_MAX_DIMS * sizeof(size_t));
|
||||
|
||||
size_t *rshape = m_new(size_t, ULAB_MAX_DIMS);
|
||||
memcpy(rshape, ndarray->shape, ULAB_MAX_DIMS * sizeof(size_t));
|
||||
|
||||
int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS);
|
||||
memcpy(strides, ndarray->strides, ULAB_MAX_DIMS * sizeof(int32_t));
|
||||
|
||||
int32_t *rstrides = m_new0(int32_t, ULAB_MAX_DIMS);
|
||||
|
||||
if(axis == mp_const_none) {
|
||||
result = ndarray_new_linear_array(true_count, ndarray->dtype);
|
||||
|
||||
rstrides[ULAB_MAX_DIMS - 1] = ndarray->itemsize;
|
||||
rshape[ULAB_MAX_DIMS - 1] = 0;
|
||||
} else {
|
||||
rshape[shift_ax] = true_count;
|
||||
|
||||
result = ndarray_new_dense_ndarray(ndarray->ndim, rshape, ndarray->dtype);
|
||||
|
||||
SWAP(size_t, shape[shift_ax], shape[ULAB_MAX_DIMS - 1]);
|
||||
SWAP(size_t, rshape[shift_ax], rshape[ULAB_MAX_DIMS - 1]);
|
||||
SWAP(int32_t, strides[shift_ax], strides[ULAB_MAX_DIMS - 1]);
|
||||
|
||||
memcpy(rstrides, result->strides, ULAB_MAX_DIMS * sizeof(int32_t));
|
||||
SWAP(int32_t, rstrides[shift_ax], rstrides[ULAB_MAX_DIMS - 1]);
|
||||
}
|
||||
|
||||
uint8_t *rarray = (uint8_t *)result->array;
|
||||
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t i = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
size_t j = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
if(axis != mp_const_none) {
|
||||
iterable = mp_getiter(condition, &iter_buf);
|
||||
}
|
||||
do {
|
||||
item = mp_iternext(iterable);
|
||||
if(mp_obj_is_true(item)) {
|
||||
memcpy(rarray, array, ndarray->itemsize);
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
array += strides[ULAB_MAX_DIMS - 1];
|
||||
l++;
|
||||
} while(l < shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
array -= strides[ULAB_MAX_DIMS - 1] * shape[ULAB_MAX_DIMS - 1];
|
||||
array += strides[ULAB_MAX_DIMS - 2];
|
||||
rarray -= rstrides[ULAB_MAX_DIMS - 1] * rshape[ULAB_MAX_DIMS - 1];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
array -= strides[ULAB_MAX_DIMS - 2] * shape[ULAB_MAX_DIMS - 2];
|
||||
array += strides[ULAB_MAX_DIMS - 3];
|
||||
rarray -= rstrides[ULAB_MAX_DIMS - 2] * rshape[ULAB_MAX_DIMS - 2];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 3];
|
||||
j++;
|
||||
} while(j < shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
array -= strides[ULAB_MAX_DIMS - 3] * shape[ULAB_MAX_DIMS - 3];
|
||||
array += strides[ULAB_MAX_DIMS - 4];
|
||||
rarray -= rstrides[ULAB_MAX_DIMS - 2] * rshape[ULAB_MAX_DIMS - 2];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 3];
|
||||
i++;
|
||||
} while(i < shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif
|
||||
|
||||
m_del(size_t, shape, ULAB_MAX_DIMS);
|
||||
m_del(size_t, rshape, ULAB_MAX_DIMS);
|
||||
m_del(int32_t, strides, ULAB_MAX_DIMS);
|
||||
m_del(int32_t, rstrides, ULAB_MAX_DIMS);
|
||||
|
||||
return MP_OBJ_FROM_PTR(result);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(transform_compress_obj, 2, transform_compress);
|
||||
#endif /* ULAB_NUMPY_HAS_COMPRESS */
|
||||
|
||||
#if ULAB_NUMPY_HAS_DELETE
|
||||
static mp_obj_t transform_delete(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be an ndarray"));
|
||||
}
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
uint8_t *array = (uint8_t *)ndarray->array;
|
||||
|
||||
mp_obj_t indices = args[1].u_obj;
|
||||
|
||||
mp_obj_t axis = args[2].u_obj;
|
||||
|
||||
int8_t shift_ax;
|
||||
|
||||
size_t axis_len;
|
||||
|
||||
if(axis != mp_const_none) {
|
||||
int8_t ax = tools_get_axis(axis, ndarray->ndim);
|
||||
shift_ax = ULAB_MAX_DIMS - ndarray->ndim + ax;
|
||||
axis_len = ndarray->shape[shift_ax];
|
||||
} else {
|
||||
axis_len = ndarray->len;
|
||||
}
|
||||
|
||||
size_t index_len;
|
||||
if(mp_obj_is_int(indices)) {
|
||||
index_len = 1;
|
||||
} else {
|
||||
if(mp_obj_len_maybe(indices) == MP_OBJ_NULL) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("wrong index type"));
|
||||
}
|
||||
index_len = MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(indices));
|
||||
if (index_len == 0){
|
||||
// if the second positional argument is empty
|
||||
// return the original array
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
}
|
||||
|
||||
if(index_len > axis_len) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("wrong length of index array"));
|
||||
}
|
||||
|
||||
size_t *index_array = m_new(size_t, index_len);
|
||||
|
||||
if(mp_obj_is_int(indices)) {
|
||||
ssize_t value = (ssize_t)mp_obj_get_int(indices);
|
||||
if(value < 0) {
|
||||
value += axis_len;
|
||||
}
|
||||
if((value < 0) || (value > (ssize_t)axis_len)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("index is out of bounds"));
|
||||
} else {
|
||||
*index_array++ = (size_t)value;
|
||||
}
|
||||
} else {
|
||||
mp_obj_iter_buf_t iter_buf;
|
||||
mp_obj_t item, iterable = mp_getiter(indices, &iter_buf);
|
||||
while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
ssize_t value = (ssize_t)mp_obj_get_int(item);
|
||||
if(value < 0) {
|
||||
value += axis_len;
|
||||
}
|
||||
if((value < 0) || (value > (ssize_t)axis_len)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("index is out of bounds"));
|
||||
} else {
|
||||
*index_array++ = (size_t)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sort the array, since it is not guaranteed that the input is sorted
|
||||
HEAPSORT1(size_t, index_array, 1, index_len);
|
||||
|
||||
size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
|
||||
memcpy(shape, ndarray->shape, ULAB_MAX_DIMS * sizeof(size_t));
|
||||
|
||||
size_t *rshape = m_new(size_t, ULAB_MAX_DIMS);
|
||||
memcpy(rshape, ndarray->shape, ULAB_MAX_DIMS * sizeof(size_t));
|
||||
|
||||
int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS);
|
||||
memcpy(strides, ndarray->strides, ULAB_MAX_DIMS * sizeof(int32_t));
|
||||
|
||||
int32_t *rstrides = m_new0(int32_t, ULAB_MAX_DIMS);
|
||||
|
||||
ndarray_obj_t *result = NULL;
|
||||
|
||||
if(axis == mp_const_none) {
|
||||
result = ndarray_new_linear_array(ndarray->len - index_len, ndarray->dtype);
|
||||
rstrides[ULAB_MAX_DIMS - 1] = ndarray->itemsize;
|
||||
memset(rshape, 0, sizeof(size_t) * ULAB_MAX_DIMS);
|
||||
} else {
|
||||
rshape[shift_ax] = shape[shift_ax] - index_len;
|
||||
|
||||
result = ndarray_new_dense_ndarray(ndarray->ndim, rshape, ndarray->dtype);
|
||||
|
||||
SWAP(size_t, shape[shift_ax], shape[ULAB_MAX_DIMS - 1]);
|
||||
SWAP(size_t, rshape[shift_ax], rshape[ULAB_MAX_DIMS - 1]);
|
||||
SWAP(int32_t, strides[shift_ax], strides[ULAB_MAX_DIMS - 1]);
|
||||
|
||||
memcpy(rstrides, result->strides, ULAB_MAX_DIMS * sizeof(int32_t));
|
||||
SWAP(int32_t, rstrides[shift_ax], rstrides[ULAB_MAX_DIMS - 1]);
|
||||
}
|
||||
|
||||
uint8_t *rarray = (uint8_t *)result->array;
|
||||
index_array -= index_len;
|
||||
size_t count = 0;
|
||||
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t i = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
size_t j = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
do {
|
||||
if(count == *index_array) {
|
||||
index_array++;
|
||||
} else {
|
||||
memcpy(rarray, array, ndarray->itemsize);
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
array += strides[ULAB_MAX_DIMS - 1];
|
||||
l++;
|
||||
count++;
|
||||
} while(l < shape[ULAB_MAX_DIMS - 1]);
|
||||
if(axis != mp_const_none) {
|
||||
index_array -= index_len;
|
||||
count = 0;
|
||||
}
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
array -= strides[ULAB_MAX_DIMS - 1] * shape[ULAB_MAX_DIMS - 1];
|
||||
array += strides[ULAB_MAX_DIMS - 2];
|
||||
rarray -= rstrides[ULAB_MAX_DIMS - 1] * rshape[ULAB_MAX_DIMS - 1];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
array -= strides[ULAB_MAX_DIMS - 2] * shape[ULAB_MAX_DIMS - 2];
|
||||
array += strides[ULAB_MAX_DIMS - 3];
|
||||
rarray -= rstrides[ULAB_MAX_DIMS - 2] * rshape[ULAB_MAX_DIMS - 2];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 3];
|
||||
j++;
|
||||
} while(j < shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
array -= strides[ULAB_MAX_DIMS - 3] * shape[ULAB_MAX_DIMS - 3];
|
||||
array += strides[ULAB_MAX_DIMS - 4];
|
||||
rarray -= rstrides[ULAB_MAX_DIMS - 3] * rshape[ULAB_MAX_DIMS - 3];
|
||||
rarray += rstrides[ULAB_MAX_DIMS - 4];
|
||||
i++;
|
||||
} while(i < shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif
|
||||
|
||||
// TODO: deleting shape generates a seg fault
|
||||
// m_del(size_t, shape, ULAB_MAX_DIMS);
|
||||
m_del(size_t, rshape, ULAB_MAX_DIMS);
|
||||
m_del(int32_t, strides, ULAB_MAX_DIMS);
|
||||
m_del(int32_t, rstrides, ULAB_MAX_DIMS);
|
||||
|
||||
return MP_OBJ_FROM_PTR(result);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(transform_delete_obj, 2, transform_delete);
|
||||
#endif /* ULAB_NUMPY_HAS_DELETE */
|
||||
|
||||
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
#if ULAB_NUMPY_HAS_DOT
|
||||
//| def dot(m1: ulab.numpy.ndarray, m2: ulab.numpy.ndarray) -> Union[ulab.numpy.ndarray, _float]:
|
||||
//| """
|
||||
//| :param ~ulab.numpy.ndarray m1: a matrix, or a vector
|
||||
//| :param ~ulab.numpy.ndarray m2: a matrix, or a vector
|
||||
//|
|
||||
//| Computes the product of two matrices, or two vectors. In the letter case, the inner product is returned."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
mp_obj_t transform_dot(mp_obj_t _m1, mp_obj_t _m2) {
|
||||
// TODO: should the results be upcast?
|
||||
// This implements 2D operations only!
|
||||
if(!mp_obj_is_type(_m1, &ulab_ndarray_type) || !mp_obj_is_type(_m2, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("arguments must be ndarrays"));
|
||||
}
|
||||
ndarray_obj_t *m1 = MP_OBJ_TO_PTR(_m1);
|
||||
ndarray_obj_t *m2 = MP_OBJ_TO_PTR(_m2);
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(m1->dtype)
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(m2->dtype)
|
||||
|
||||
uint8_t *array1 = (uint8_t *)m1->array;
|
||||
uint8_t *array2 = (uint8_t *)m2->array;
|
||||
|
||||
mp_float_t (*func1)(void *) = ndarray_get_float_function(m1->dtype);
|
||||
mp_float_t (*func2)(void *) = ndarray_get_float_function(m2->dtype);
|
||||
|
||||
if(m1->shape[ULAB_MAX_DIMS - 1] != m2->shape[ULAB_MAX_DIMS - m2->ndim]) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("dimensions do not match"));
|
||||
}
|
||||
uint8_t ndim = MIN(m1->ndim, m2->ndim);
|
||||
size_t shape1 = m1->ndim == 2 ? m1->shape[ULAB_MAX_DIMS - m1->ndim] : 1;
|
||||
size_t shape2 = m2->ndim == 2 ? m2->shape[ULAB_MAX_DIMS - 1] : 1;
|
||||
|
||||
size_t *shape = NULL;
|
||||
if(ndim == 2) { // matrix times matrix -> matrix
|
||||
shape = ndarray_shape_vector(0, 0, shape1, shape2);
|
||||
} else { // matrix times vector -> vector, vector times vector -> vector (size 1)
|
||||
shape = ndarray_shape_vector(0, 0, 0, shape1 * shape2);
|
||||
}
|
||||
ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
|
||||
mp_float_t *rarray = (mp_float_t *)results->array;
|
||||
|
||||
for(size_t i=0; i < shape1; i++) { // rows of m1
|
||||
for(size_t j=0; j < shape2; j++) { // columns of m2
|
||||
mp_float_t dot = 0.0;
|
||||
for(size_t k=0; k < m1->shape[ULAB_MAX_DIMS - 1]; k++) {
|
||||
// (i, k) * (k, j)
|
||||
dot += func1(array1) * func2(array2);
|
||||
array1 += m1->strides[ULAB_MAX_DIMS - 1];
|
||||
array2 += m2->strides[ULAB_MAX_DIMS - m2->ndim];
|
||||
}
|
||||
*rarray++ = dot;
|
||||
array1 -= m1->strides[ULAB_MAX_DIMS - 1] * m1->shape[ULAB_MAX_DIMS - 1];
|
||||
array2 -= m2->strides[ULAB_MAX_DIMS - m2->ndim] * m2->shape[ULAB_MAX_DIMS - m2->ndim];
|
||||
array2 += m2->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
array1 += m1->strides[ULAB_MAX_DIMS - m1->ndim];
|
||||
array2 = m2->array;
|
||||
}
|
||||
if((m1->ndim * m2->ndim) == 1) { // return a scalar, if product of two vectors
|
||||
return mp_obj_new_float(*(--rarray));
|
||||
} else {
|
||||
return MP_OBJ_FROM_PTR(results);
|
||||
}
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(transform_dot_obj, transform_dot);
|
||||
#endif /* ULAB_NUMPY_HAS_DOT */
|
||||
#endif /* ULAB_MAX_DIMS > 1 */
|
||||
|
||||
#if ULAB_NUMPY_HAS_SIZE
|
||||
static mp_obj_t transform_size(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
if(ulab_tools_mp_obj_is_scalar(args[0].u_obj)) {
|
||||
return mp_obj_new_int(1);
|
||||
}
|
||||
|
||||
if(!ndarray_object_is_array_like(args[0].u_obj)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be an ndarray"));
|
||||
}
|
||||
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
|
||||
return mp_obj_len_maybe(args[0].u_obj);
|
||||
}
|
||||
|
||||
// at this point, the args[0] is most certainly an ndarray
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
mp_obj_t axis = args[1].u_obj;
|
||||
size_t len;
|
||||
if(axis != mp_const_none) {
|
||||
int8_t ax = tools_get_axis(axis, ndarray->ndim);
|
||||
len = ndarray->shape[ULAB_MAX_DIMS - ndarray->ndim + ax];
|
||||
} else {
|
||||
len = ndarray->len;
|
||||
}
|
||||
|
||||
return mp_obj_new_int(len);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(transform_size_obj, 1, transform_size);
|
||||
#endif
|
||||
30
code/numpy/transform.h
Normal file
30
code/numpy/transform.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2021 Zoltán Vörös
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _TRANSFORM_
|
||||
#define _TRANSFORM_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "../ulab_tools.h"
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(transform_compress_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(transform_delete_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(transform_dot_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(transform_size_obj);
|
||||
|
||||
#endif
|
||||
978
code/numpy/vector.c
Normal file
978
code/numpy/vector.c
Normal file
|
|
@ -0,0 +1,978 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2023 Zoltán Vörös
|
||||
* 2020-2023 Jeff Epler for Adafruit Industries
|
||||
* 2020 Scott Shawcroft for Adafruit Industries
|
||||
* 2020 Taku Fukada
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "py/runtime.h"
|
||||
#include "py/binary.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/objarray.h"
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "../ulab_tools.h"
|
||||
#include "carray/carray_tools.h"
|
||||
#include "vector.h"
|
||||
|
||||
//| """Element-by-element functions
|
||||
//|
|
||||
//| These functions can operate on numbers, 1-D iterables, and arrays of 1 to 4 dimensions by
|
||||
//| applying the function to every element in the array. This is typically
|
||||
//| much more efficient than expressing the same operation as a Python loop."""
|
||||
//|
|
||||
|
||||
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
static mp_obj_t vector_generic_vector(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, mp_float_t (*f)(mp_float_t)) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE} } ,
|
||||
{ MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
// this keyword argument is not used; it's only here, so that functions that
|
||||
// support the complex dtype can call vector_generic_vector directly
|
||||
{ MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
mp_obj_t o_in = args[0].u_obj;
|
||||
|
||||
// Return a single value, if o_in is not iterable
|
||||
if(mp_obj_is_float(o_in) || mp_obj_is_int(o_in)) {
|
||||
return mp_obj_new_float(f(mp_obj_get_float(o_in)));
|
||||
}
|
||||
mp_obj_t out = args[1].u_obj;
|
||||
|
||||
ndarray_obj_t *target = NULL;
|
||||
ndarray_obj_t *source = NULL;
|
||||
|
||||
if(mp_obj_is_type(o_in, &ulab_ndarray_type)) {
|
||||
source = MP_OBJ_TO_PTR(o_in);
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(source->dtype)
|
||||
if(out == mp_const_none) {
|
||||
target = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT);
|
||||
} else {
|
||||
if(!mp_obj_is_type(out, &ulab_ndarray_type)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("out must be an ndarray"));
|
||||
}
|
||||
target = MP_OBJ_TO_PTR(out);
|
||||
if(target->dtype != NDARRAY_FLOAT) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("out must be of float dtype"));
|
||||
}
|
||||
if(target->ndim != source->ndim) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("input and output dimensions differ"));
|
||||
}
|
||||
for(uint8_t d = 0; d < target->ndim; d++) {
|
||||
if(target->shape[ULAB_MAX_DIMS - 1 - d] != source->shape[ULAB_MAX_DIMS - 1 - d]) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("input and output shapes differ"));
|
||||
}
|
||||
}
|
||||
}
|
||||
mp_float_t *tarray = (mp_float_t *)target->array;
|
||||
int32_t *tstrides = m_new(int32_t, ULAB_MAX_DIMS);
|
||||
for(uint8_t d = 0; d < target->ndim; d++) {
|
||||
tstrides[ULAB_MAX_DIMS - 1 - d] = target->strides[ULAB_MAX_DIMS - 1 - d] / target->itemsize;
|
||||
}
|
||||
|
||||
uint8_t *sarray = (uint8_t *)source->array;
|
||||
|
||||
#if ULAB_VECTORISE_USES_FUN_POINTER
|
||||
|
||||
mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype);
|
||||
|
||||
ITERATOR_HEAD();
|
||||
mp_float_t value = func(sarray);
|
||||
*tarray++ = f(value);
|
||||
ITERATOR_TAIL(source, sarray);
|
||||
#else
|
||||
if(source->dtype == NDARRAY_UINT8) {
|
||||
ITERATE_VECTOR(uint8_t, target, tarray, tstrides, source, sarray);
|
||||
} else if(source->dtype == NDARRAY_INT8) {
|
||||
ITERATE_VECTOR(int8_t, target, tarray, tstrides, source, sarray);
|
||||
} else if(source->dtype == NDARRAY_UINT16) {
|
||||
ITERATE_VECTOR(uint16_t, target, tarray, tstrides, source, sarray);
|
||||
} else if(source->dtype == NDARRAY_INT16) {
|
||||
ITERATE_VECTOR(int16_t, target, tarray, tstrides, source, sarray);
|
||||
} else {
|
||||
ITERATE_VECTOR(mp_float_t, target, tarray, tstrides, source, sarray);
|
||||
}
|
||||
#endif /* ULAB_VECTORISE_USES_FUN_POINTER */
|
||||
} else {
|
||||
target = ndarray_from_mp_obj(o_in, 0);
|
||||
mp_float_t *tarray = (mp_float_t *)target->array;
|
||||
for(size_t i = 0; i < target->len; i++) {
|
||||
*tarray = f(*tarray);
|
||||
tarray++;
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(target);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static mp_obj_t vector_generic_vector(mp_obj_t o_in, mp_float_t (*f)(mp_float_t)) {
|
||||
// Return a single value, if o_in is not iterable
|
||||
if(mp_obj_is_float(o_in) || mp_obj_is_int(o_in)) {
|
||||
return mp_obj_new_float(f(mp_obj_get_float(o_in)));
|
||||
}
|
||||
ndarray_obj_t *ndarray = NULL;
|
||||
if(mp_obj_is_type(o_in, &ulab_ndarray_type)) {
|
||||
ndarray_obj_t *source = MP_OBJ_TO_PTR(o_in);
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(source->dtype)
|
||||
uint8_t *sarray = (uint8_t *)source->array;
|
||||
ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT);
|
||||
mp_float_t *array = (mp_float_t *)ndarray->array;
|
||||
|
||||
#if ULAB_VECTORISE_USES_FUN_POINTER
|
||||
|
||||
mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype);
|
||||
|
||||
ITERATOR_HEAD();
|
||||
mp_float_t value = func(sarray);
|
||||
*array++ = f(value);
|
||||
ITERATOR_TAIL(source, sarray);
|
||||
#else
|
||||
if(source->dtype == NDARRAY_UINT8) {
|
||||
ITERATE_VECTOR(uint8_t, array, source, sarray);
|
||||
} else if(source->dtype == NDARRAY_INT8) {
|
||||
ITERATE_VECTOR(int8_t, array, source, sarray);
|
||||
} else if(source->dtype == NDARRAY_UINT16) {
|
||||
ITERATE_VECTOR(uint16_t, array, source, sarray);
|
||||
} else if(source->dtype == NDARRAY_INT16) {
|
||||
ITERATE_VECTOR(int16_t, array, source, sarray);
|
||||
} else {
|
||||
ITERATE_VECTOR(mp_float_t, array, source, sarray);
|
||||
}
|
||||
#endif /* ULAB_VECTORISE_USES_FUN_POINTER */
|
||||
} else {
|
||||
ndarray = ndarray_from_mp_obj(o_in, 0);
|
||||
mp_float_t *narray = (mp_float_t *)ndarray->array;
|
||||
for(size_t i = 0; i < ndarray->len; i++) {
|
||||
*narray = f(*narray);
|
||||
narray++;
|
||||
}
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
|
||||
|
||||
#if ULAB_NUMPY_HAS_ACOS
|
||||
//| def acos(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes the inverse cosine function"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(acos, acos);
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_acos_obj, 1, vector_acos);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_acos_obj, vector_acos);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_NUMPY_HAS_ACOS */
|
||||
|
||||
#if ULAB_NUMPY_HAS_ACOSH
|
||||
//| def acosh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes the inverse hyperbolic cosine function"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(acosh, acosh);
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_acosh_obj, 1, vector_acosh);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_acosh_obj, vector_acosh);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_NUMPY_HAS_ACOSH */
|
||||
|
||||
#if ULAB_NUMPY_HAS_ASIN
|
||||
//| def asin(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes the inverse sine function"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(asin, asin);
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_asin_obj, 1, vector_asin);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_asin_obj, vector_asin);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_NUMPY_HAS_ASIN */
|
||||
|
||||
#if ULAB_NUMPY_HAS_ASINH
|
||||
//| def asinh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes the inverse hyperbolic sine function"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(asinh, asinh);
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_asinh_obj, 1, vector_asinh);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_asinh_obj, vector_asinh);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_NUMPY_HAS_ASINH */
|
||||
|
||||
#if ULAB_NUMPY_HAS_AROUND
|
||||
//| def around(a: ulab.numpy.ndarray, *, decimals: int = 0) -> ulab.numpy.ndarray:
|
||||
//| """Returns a new float array in which each element is rounded to
|
||||
//| ``decimals`` places."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
mp_obj_t vector_around(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
||||
{ MP_QSTR_decimals, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0 } },
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
{ MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }
|
||||
#endif
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be an ndarray"));
|
||||
}
|
||||
int8_t n = args[1].u_int;
|
||||
mp_float_t mul = MICROPY_FLOAT_C_FUN(pow)(10.0, n);
|
||||
ndarray_obj_t *source = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(source->dtype)
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
mp_obj_t out = args[2].u_obj;
|
||||
if(out != mp_const_none) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("out keyword is not supported for function"));
|
||||
}
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT);
|
||||
mp_float_t *narray = (mp_float_t *)ndarray->array;
|
||||
uint8_t *sarray = (uint8_t *)source->array;
|
||||
|
||||
mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype);
|
||||
|
||||
ITERATOR_HEAD();
|
||||
mp_float_t f = func(sarray);
|
||||
*narray++ = MICROPY_FLOAT_C_FUN(round)(f * mul) / mul;
|
||||
ITERATOR_TAIL(source, sarray);
|
||||
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_around_obj, 1, vector_around);
|
||||
#endif /* ULAB_NUMPY_HAS_AROUND */
|
||||
|
||||
#if ULAB_NUMPY_HAS_ATAN
|
||||
//| def atan(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes the inverse tangent function; the return values are in the
|
||||
//| range [-pi/2,pi/2]."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(atan, atan);
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_atan_obj, 1, vector_atan);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_atan_obj, vector_atan);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_NUMPY_HAS_ATAN */
|
||||
|
||||
|
||||
#if ULAB_NUMPY_HAS_ATANH
|
||||
//| def atanh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes the inverse hyperbolic tangent function"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(atanh, atanh);
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_atanh_obj, 1, vector_atanh);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_atanh_obj, vector_atanh);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_NUMPY_HAS_ATANH */
|
||||
|
||||
#if ULAB_NUMPY_HAS_ARCTAN2
|
||||
//| def arctan2(ya: _ScalarOrArrayLike, xa: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes the inverse tangent function of y/x; the return values are in
|
||||
//| the range [-pi, pi]."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
mp_obj_t vector_arctan2(mp_obj_t y, mp_obj_t x) {
|
||||
if((mp_obj_is_float(y) || mp_obj_is_int(y)) &&
|
||||
(mp_obj_is_float(x) || mp_obj_is_int(x))) {
|
||||
mp_float_t _y = mp_obj_get_float(y);
|
||||
mp_float_t _x = mp_obj_get_float(x);
|
||||
return mp_obj_new_float(MICROPY_FLOAT_C_FUN(atan2)(_y, _x));
|
||||
}
|
||||
|
||||
ndarray_obj_t *ndarray_x = ndarray_from_mp_obj(x, 0);
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray_x->dtype)
|
||||
|
||||
ndarray_obj_t *ndarray_y = ndarray_from_mp_obj(y, 0);
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray_y->dtype)
|
||||
|
||||
uint8_t ndim = 0;
|
||||
size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
|
||||
int32_t *xstrides = m_new(int32_t, ULAB_MAX_DIMS);
|
||||
int32_t *ystrides = m_new(int32_t, ULAB_MAX_DIMS);
|
||||
if(!ndarray_can_broadcast(ndarray_x, ndarray_y, &ndim, shape, xstrides, ystrides)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("operands could not be broadcast together"));
|
||||
m_del(size_t, shape, ULAB_MAX_DIMS);
|
||||
m_del(int32_t, xstrides, ULAB_MAX_DIMS);
|
||||
m_del(int32_t, ystrides, ULAB_MAX_DIMS);
|
||||
}
|
||||
|
||||
uint8_t *xarray = (uint8_t *)ndarray_x->array;
|
||||
uint8_t *yarray = (uint8_t *)ndarray_y->array;
|
||||
|
||||
ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
|
||||
mp_float_t *rarray = (mp_float_t *)results->array;
|
||||
|
||||
mp_float_t (*funcx)(void *) = ndarray_get_float_function(ndarray_x->dtype);
|
||||
mp_float_t (*funcy)(void *) = ndarray_get_float_function(ndarray_y->dtype);
|
||||
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
size_t i = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
size_t j = 0;
|
||||
do {
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
size_t k = 0;
|
||||
do {
|
||||
#endif
|
||||
size_t l = 0;
|
||||
do {
|
||||
mp_float_t _x = funcx(xarray);
|
||||
mp_float_t _y = funcy(yarray);
|
||||
*rarray++ = MICROPY_FLOAT_C_FUN(atan2)(_y, _x);
|
||||
xarray += xstrides[ULAB_MAX_DIMS - 1];
|
||||
yarray += ystrides[ULAB_MAX_DIMS - 1];
|
||||
l++;
|
||||
} while(l < results->shape[ULAB_MAX_DIMS - 1]);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
xarray -= xstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
|
||||
xarray += xstrides[ULAB_MAX_DIMS - 2];
|
||||
yarray -= ystrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
|
||||
yarray += ystrides[ULAB_MAX_DIMS - 2];
|
||||
k++;
|
||||
} while(k < results->shape[ULAB_MAX_DIMS - 2]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 2
|
||||
xarray -= xstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
|
||||
xarray += xstrides[ULAB_MAX_DIMS - 3];
|
||||
yarray -= ystrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
|
||||
yarray += ystrides[ULAB_MAX_DIMS - 3];
|
||||
j++;
|
||||
} while(j < results->shape[ULAB_MAX_DIMS - 3]);
|
||||
#endif
|
||||
#if ULAB_MAX_DIMS > 3
|
||||
xarray -= xstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
|
||||
xarray += xstrides[ULAB_MAX_DIMS - 4];
|
||||
yarray -= ystrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
|
||||
yarray += ystrides[ULAB_MAX_DIMS - 4];
|
||||
i++;
|
||||
} while(i < results->shape[ULAB_MAX_DIMS - 4]);
|
||||
#endif
|
||||
|
||||
return MP_OBJ_FROM_PTR(results);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(vector_arctan2_obj, vector_arctan2);
|
||||
#endif /* ULAB_VECTORISE_HAS_ARCTAN2 */
|
||||
|
||||
#if ULAB_NUMPY_HAS_CEIL
|
||||
//| def ceil(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Rounds numbers up to the next whole number"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(ceil, ceil);
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_ceil_obj, 1, vector_ceil);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_ceil_obj, vector_ceil);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_NUMPY_HAS_CEIL */
|
||||
|
||||
#if ULAB_NUMPY_HAS_COS
|
||||
//| def cos(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes the cosine function"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(cos, cos);
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_cos_obj, 1, vector_cos);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_cos_obj, vector_cos);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_NUMPY_HAS_COS */
|
||||
|
||||
#if ULAB_NUMPY_HAS_COSH
|
||||
//| def cosh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes the hyperbolic cosine function"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(cosh, cosh);
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_cosh_obj, 1, vector_cosh);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_cosh_obj, vector_cosh);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_NUMPY_HAS_COSH */
|
||||
|
||||
#if ULAB_NUMPY_HAS_DEGREES
|
||||
//| def degrees(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Converts angles from radians to degrees"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_float_t vector_degrees_(mp_float_t value) {
|
||||
return value * MICROPY_FLOAT_CONST(180.0) / MP_PI;
|
||||
}
|
||||
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
static mp_obj_t vector_degrees(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return vector_generic_vector(n_args, pos_args, kw_args, vector_degrees_);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_degrees_obj, 1, vector_degrees);
|
||||
#else
|
||||
static mp_obj_t vector_degrees(mp_obj_t x_obj) {
|
||||
return vector_generic_vector(x_obj, vector_degrees_);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_degrees_obj, vector_degrees);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_NUMPY_HAS_DEGREES */
|
||||
|
||||
#if ULAB_SCIPY_SPECIAL_HAS_ERF
|
||||
//| def erf(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes the error function, which has applications in statistics"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(erf, erf);
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_erf_obj, 1, vector_erf);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_erf_obj, vector_erf);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_SCIPY_SPECIAL_HAS_ERF */
|
||||
|
||||
#if ULAB_SCIPY_SPECIAL_HAS_ERFC
|
||||
//| def erfc(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes the complementary error function, which has applications in statistics"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(erfc, erfc);
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_erfc_obj, 1, vector_erfc);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_erfc_obj, vector_erfc);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_SCIPY_SPECIAL_HAS_ERFC */
|
||||
|
||||
#if ULAB_NUMPY_HAS_EXP
|
||||
//| def exp(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes the exponent function."""
|
||||
//| ...
|
||||
//|
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
static mp_obj_t vector_exp(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
#else
|
||||
static mp_obj_t vector_exp(mp_obj_t o_in) {
|
||||
#endif
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
// since the complex case is dissimilar to the rest, we've got to do the parsing of the keywords here
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE} } ,
|
||||
{ MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
mp_obj_t o_in = args[0].u_obj;
|
||||
mp_obj_t out = args[1].u_obj;
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
|
||||
if(mp_obj_is_type(o_in, &mp_type_complex)) {
|
||||
mp_float_t real, imag;
|
||||
mp_obj_get_complex(o_in, &real, &imag);
|
||||
mp_float_t exp_real = MICROPY_FLOAT_C_FUN(exp)(real);
|
||||
return mp_obj_new_complex(exp_real * MICROPY_FLOAT_C_FUN(cos)(imag), exp_real * MICROPY_FLOAT_C_FUN(sin)(imag));
|
||||
} else if(mp_obj_is_type(o_in, &ulab_ndarray_type)) {
|
||||
ndarray_obj_t *source = MP_OBJ_TO_PTR(o_in);
|
||||
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
if((out != mp_const_none) && (source->dtype == NDARRAY_COMPLEX)){
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("out keyword is not supported for complex dtype"));
|
||||
}
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
|
||||
if(source->dtype == NDARRAY_COMPLEX) {
|
||||
uint8_t *sarray = (uint8_t *)source->array;
|
||||
ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_COMPLEX);
|
||||
mp_float_t *array = (mp_float_t *)ndarray->array;
|
||||
uint8_t itemsize = sizeof(mp_float_t);
|
||||
|
||||
ITERATOR_HEAD();
|
||||
mp_float_t real = *(mp_float_t *)sarray;
|
||||
mp_float_t imag = *(mp_float_t *)(sarray + itemsize);
|
||||
mp_float_t exp_real = MICROPY_FLOAT_C_FUN(exp)(real);
|
||||
*array++ = exp_real * MICROPY_FLOAT_C_FUN(cos)(imag);
|
||||
*array++ = exp_real * MICROPY_FLOAT_C_FUN(sin)(imag);
|
||||
ITERATOR_TAIL(source, sarray);
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
}
|
||||
#endif /* ULAB_SUPPORTS_COMPLEX */
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
return vector_generic_vector(n_args, pos_args, kw_args, MICROPY_FLOAT_C_FUN(exp));
|
||||
#else
|
||||
return vector_generic_vector(o_in, MICROPY_FLOAT_C_FUN(exp));
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
}
|
||||
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_exp_obj, 1, vector_exp);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_exp_obj, vector_exp);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_NUMPY_HAS_EXP */
|
||||
|
||||
#if ULAB_NUMPY_HAS_EXPM1
|
||||
//| def expm1(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes $e^x-1$. In certain applications, using this function preserves numeric accuracy better than the `exp` function."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(expm1, expm1);
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_expm1_obj, 1, vector_expm1);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_expm1_obj, vector_expm1);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_NUMPY_HAS_EXPM1 */
|
||||
|
||||
#if ULAB_NUMPY_HAS_FLOOR
|
||||
//| def floor(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Rounds numbers up to the next whole number"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(floor, floor);
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_floor_obj, 1, vector_floor);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_floor_obj, vector_floor);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_NUMPY_HAS_FLOOR */
|
||||
|
||||
#if ULAB_SCIPY_SPECIAL_HAS_GAMMA
|
||||
//| def gamma(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes the gamma function"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(gamma, tgamma);
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_gamma_obj, 1, vector_gamma);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_gamma_obj, vector_gamma);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_SCIPY_SPECIAL_HAS_GAMMA */
|
||||
|
||||
#if ULAB_SCIPY_SPECIAL_HAS_GAMMALN
|
||||
//| def lgamma(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes the natural log of the gamma function"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(lgamma, lgamma);
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_lgamma_obj, 1, vector_lgamma);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_lgamma_obj, vector_lgamma);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_SCIPY_SEPCIAL_HAS_GAMMALN */
|
||||
|
||||
#if ULAB_NUMPY_HAS_LOG
|
||||
//| def log(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes the natural log"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(log, log);
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_log_obj, 1, vector_log);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_log_obj, vector_log);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_NUMPY_HAS_LOG */
|
||||
|
||||
#if ULAB_NUMPY_HAS_LOG10
|
||||
//| def log10(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes the log base 10"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(log10, log10);
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_log10_obj, 1, vector_log10);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_log10_obj, vector_log10);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_NUMPY_HAS_LOG10 */
|
||||
|
||||
#if ULAB_NUMPY_HAS_LOG2
|
||||
//| def log2(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes the log base 2"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(log2, log2);
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_log2_obj, 1, vector_log2);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_log2_obj, vector_log2);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_NUMPY_HAS_LOG2 */
|
||||
|
||||
#if ULAB_NUMPY_HAS_RADIANS
|
||||
//| def radians(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Converts angles from degrees to radians"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_float_t vector_radians_(mp_float_t value) {
|
||||
return value * MP_PI / MICROPY_FLOAT_CONST(180.0);
|
||||
}
|
||||
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
static mp_obj_t vector_radians(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return vector_generic_vector(n_args, pos_args, kw_args, vector_radians_);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_radians_obj, 1, vector_radians);
|
||||
#else
|
||||
static mp_obj_t vector_radians(mp_obj_t x_obj) {
|
||||
return vector_generic_vector(x_obj, vector_radians_);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_radians_obj, vector_radians);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_NUMPY_HAS_RADIANS */
|
||||
|
||||
#if ULAB_NUMPY_HAS_SIN
|
||||
//| def sin(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes the sine function"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(sin, sin);
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_sin_obj, 1, vector_sin);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_sin_obj, vector_sin);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_NUMPY_HAS_SIN */
|
||||
|
||||
#if ULAB_NUMPY_HAS_SINC
|
||||
//| def sinc(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes the normalized sinc function"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_float_t vector_sinc1(mp_float_t x) {
|
||||
if (x == MICROPY_FLOAT_CONST(0.)) {
|
||||
return MICROPY_FLOAT_CONST(1.);
|
||||
}
|
||||
x *= MP_PI;
|
||||
return MICROPY_FLOAT_C_FUN(sin)(x) / x;
|
||||
}
|
||||
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
static mp_obj_t vector_sinc(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return vector_generic_vector(n_args, pos_args, kw_args, vector_sinc1);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_sinc_obj, 1, vector_sinc);
|
||||
#else
|
||||
static mp_obj_t vector_sinc(mp_obj_t x_obj) {
|
||||
return vector_generic_vector(x_obj, vector_sinc1);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_sinc_obj, vector_sinc);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_NUMPY_HAS_SINC */
|
||||
|
||||
#if ULAB_NUMPY_HAS_SINH
|
||||
//| def sinh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes the hyperbolic sine"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(sinh, sinh);
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_sinh_obj, 1, vector_sinh);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_sinh_obj, vector_sinh);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_NUMPY_HAS_SINH */
|
||||
|
||||
|
||||
#if ULAB_NUMPY_HAS_SQRT
|
||||
//| def sqrt(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes the square root"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
#if ULAB_SUPPORTS_COMPLEX | ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
mp_obj_t vector_sqrt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
{ MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
#endif
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
{ MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_INT(NDARRAY_FLOAT) } },
|
||||
#endif
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
mp_obj_t o_in = args[0].u_obj;
|
||||
uint8_t dtype = mp_obj_get_int(args[2].u_obj);
|
||||
if((dtype != NDARRAY_FLOAT) && (dtype != NDARRAY_COMPLEX)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("dtype must be float, or complex"));
|
||||
}
|
||||
|
||||
if(mp_obj_is_type(o_in, &mp_type_complex)) {
|
||||
mp_float_t real, imag;
|
||||
mp_obj_get_complex(o_in, &real, &imag);
|
||||
mp_float_t sqrt_abs = MICROPY_FLOAT_C_FUN(sqrt)(real * real + imag * imag);
|
||||
sqrt_abs = MICROPY_FLOAT_C_FUN(sqrt)(sqrt_abs);
|
||||
mp_float_t theta = MICROPY_FLOAT_CONST(0.5) * MICROPY_FLOAT_C_FUN(atan2)(imag, real);
|
||||
return mp_obj_new_complex(sqrt_abs * MICROPY_FLOAT_C_FUN(cos)(theta), sqrt_abs * MICROPY_FLOAT_C_FUN(sin)(theta));
|
||||
} else if(mp_obj_is_type(o_in, &ulab_ndarray_type)) {
|
||||
ndarray_obj_t *source = MP_OBJ_TO_PTR(o_in);
|
||||
if((source->dtype == NDARRAY_COMPLEX) && (dtype == NDARRAY_FLOAT)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("can't convert complex to float"));
|
||||
}
|
||||
|
||||
if(dtype == NDARRAY_COMPLEX) {
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
mp_obj_t out = args[1].u_obj;
|
||||
if(out != mp_const_none) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("out keyword is not supported for complex dtype"));
|
||||
}
|
||||
#endif
|
||||
if(source->dtype == NDARRAY_COMPLEX) {
|
||||
uint8_t *sarray = (uint8_t *)source->array;
|
||||
ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_COMPLEX);
|
||||
mp_float_t *array = (mp_float_t *)ndarray->array;
|
||||
uint8_t itemsize = sizeof(mp_float_t);
|
||||
|
||||
ITERATOR_HEAD();
|
||||
mp_float_t real = *(mp_float_t *)sarray;
|
||||
mp_float_t imag = *(mp_float_t *)(sarray + itemsize);
|
||||
mp_float_t sqrt_abs = MICROPY_FLOAT_C_FUN(sqrt)(real * real + imag * imag);
|
||||
sqrt_abs = MICROPY_FLOAT_C_FUN(sqrt)(sqrt_abs);
|
||||
mp_float_t theta = MICROPY_FLOAT_CONST(0.5) * MICROPY_FLOAT_C_FUN(atan2)(imag, real);
|
||||
*array++ = sqrt_abs * MICROPY_FLOAT_C_FUN(cos)(theta);
|
||||
*array++ = sqrt_abs * MICROPY_FLOAT_C_FUN(sin)(theta);
|
||||
ITERATOR_TAIL(source, sarray);
|
||||
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
} else if(source->dtype == NDARRAY_FLOAT) {
|
||||
uint8_t *sarray = (uint8_t *)source->array;
|
||||
ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_COMPLEX);
|
||||
mp_float_t *array = (mp_float_t *)ndarray->array;
|
||||
|
||||
ITERATOR_HEAD();
|
||||
mp_float_t value = *(mp_float_t *)sarray;
|
||||
if(value >= MICROPY_FLOAT_CONST(0.0)) {
|
||||
*array++ = MICROPY_FLOAT_C_FUN(sqrt)(value);
|
||||
array++;
|
||||
} else {
|
||||
array++;
|
||||
*array++ = MICROPY_FLOAT_C_FUN(sqrt)(-value);
|
||||
}
|
||||
ITERATOR_TAIL(source, sarray);
|
||||
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
} else {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("input dtype must be float or complex"));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* ULAB_SUPPORTS_COMPLEX */
|
||||
return vector_generic_vector(n_args, pos_args, kw_args, MICROPY_FLOAT_C_FUN(sqrt));
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_sqrt_obj, 1, vector_sqrt);
|
||||
#else
|
||||
MATH_FUN_1(sqrt, sqrt);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_sqrt_obj, vector_sqrt);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD | ULAB_SUPPORTS_COMPLEX */
|
||||
#endif /* ULAB_NUMPY_HAS_SQRT */
|
||||
|
||||
#if ULAB_NUMPY_HAS_TAN
|
||||
//| def tan(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes the tangent"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
MATH_FUN_1(tan, tan);
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_tan_obj, 1, vector_tan);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_tan_obj, vector_tan);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_NUMPY_HAS_TAN */
|
||||
|
||||
#if ULAB_NUMPY_HAS_TANH
|
||||
//| def tanh(a: _ScalarOrArrayLike) -> _ScalarOrNdArray:
|
||||
//| """Computes the hyperbolic tangent"""
|
||||
//| ...
|
||||
|
||||
MATH_FUN_1(tanh, tanh);
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_tanh_obj, 1, vector_tanh);
|
||||
#else
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vector_tanh_obj, vector_tanh);
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* ULAB_NUMPY_HAS_TANH */
|
||||
|
||||
#if ULAB_NUMPY_HAS_VECTORIZE
|
||||
static mp_obj_t vector_vectorized_function_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
(void) n_args;
|
||||
(void) n_kw;
|
||||
vectorized_function_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_obj_t avalue[1];
|
||||
mp_obj_t fvalue;
|
||||
if(mp_obj_is_type(args[0], &ulab_ndarray_type)) {
|
||||
ndarray_obj_t *source = MP_OBJ_TO_PTR(args[0]);
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(source->dtype)
|
||||
|
||||
ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, self->otypes);
|
||||
uint8_t *sarray = (uint8_t *)source->array;
|
||||
uint8_t *narray = (uint8_t *)ndarray->array;
|
||||
|
||||
ITERATOR_HEAD();
|
||||
avalue[0] = mp_binary_get_val_array(source->dtype, sarray, 0);
|
||||
fvalue = MP_OBJ_TYPE_GET_SLOT(self->type, call)(self->fun, 1, 0, avalue);
|
||||
ndarray_set_value(self->otypes, narray, 0, fvalue);
|
||||
narray += ndarray->itemsize;
|
||||
ITERATOR_TAIL(source, sarray);
|
||||
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
} else if(mp_obj_is_type(args[0], &mp_type_tuple) || mp_obj_is_type(args[0], &mp_type_list) ||
|
||||
mp_obj_is_type(args[0], &mp_type_range)) { // i.e., the input is a generic iterable
|
||||
size_t len = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[0]));
|
||||
ndarray_obj_t *ndarray = ndarray_new_linear_array(len, self->otypes);
|
||||
mp_obj_iter_buf_t iter_buf;
|
||||
mp_obj_t iterable = mp_getiter(args[0], &iter_buf);
|
||||
size_t i=0;
|
||||
while ((avalue[0] = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
fvalue = MP_OBJ_TYPE_GET_SLOT(self->type, call)(self->fun, 1, 0, avalue);
|
||||
ndarray_set_value(self->otypes, ndarray->array, i, fvalue);
|
||||
i++;
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
} else if(mp_obj_is_int(args[0]) || mp_obj_is_float(args[0])) {
|
||||
ndarray_obj_t *ndarray = ndarray_new_linear_array(1, self->otypes);
|
||||
fvalue = MP_OBJ_TYPE_GET_SLOT(self->type, call)(self->fun, 1, 0, args);
|
||||
ndarray_set_value(self->otypes, ndarray->array, 0, fvalue);
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
} else {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("wrong input type"));
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
#if defined(MP_DEFINE_CONST_OBJ_TYPE)
|
||||
MP_DEFINE_CONST_OBJ_TYPE(
|
||||
vector_function_type,
|
||||
MP_QSTR_,
|
||||
MP_TYPE_FLAG_NONE,
|
||||
call, vector_vectorized_function_call
|
||||
);
|
||||
#else
|
||||
const mp_obj_type_t vector_function_type = {
|
||||
{ &mp_type_type },
|
||||
.flags = MP_TYPE_FLAG_EXTENDED,
|
||||
.name = MP_QSTR_,
|
||||
MP_TYPE_EXTENDED_FIELDS(
|
||||
.call = vector_vectorized_function_call,
|
||||
)
|
||||
};
|
||||
#endif
|
||||
|
||||
//| def vectorize(
|
||||
//| f: Union[Callable[[int], _float], Callable[[_float], _float]],
|
||||
//| *,
|
||||
//| otypes: Optional[_DType] = None
|
||||
//| ) -> Callable[[_ScalarOrArrayLike], ulab.numpy.ndarray]:
|
||||
//| """
|
||||
//| :param callable f: The function to wrap
|
||||
//| :param otypes: List of array types that may be returned by the function. None is interpreted to mean the return value is float.
|
||||
//|
|
||||
//| Wrap a Python function ``f`` so that it can be applied to arrays or scalars. A scalar passed to the wrapped function is treated as a single-element 1-D array.
|
||||
//| The callable must return only values of the types specified by ``otypes``, or the result is undefined."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t vector_vectorize(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
||||
{ MP_QSTR_otypes, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }
|
||||
};
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
const mp_obj_type_t *type = mp_obj_get_type(args[0].u_obj);
|
||||
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a callable"));
|
||||
}
|
||||
mp_obj_t _otypes = args[1].u_obj;
|
||||
uint8_t otypes = NDARRAY_FLOAT;
|
||||
if(_otypes == mp_const_none) {
|
||||
// TODO: is this what numpy does?
|
||||
otypes = NDARRAY_FLOAT;
|
||||
} else if(mp_obj_is_int(_otypes)) {
|
||||
otypes = mp_obj_get_int(_otypes);
|
||||
if(otypes != NDARRAY_FLOAT && otypes != NDARRAY_UINT8 && otypes != NDARRAY_INT8 &&
|
||||
otypes != NDARRAY_UINT16 && otypes != NDARRAY_INT16) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("wrong output type"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("wrong output type"));
|
||||
}
|
||||
vectorized_function_obj_t *function = m_new_obj(vectorized_function_obj_t);
|
||||
function->base.type = &vector_function_type;
|
||||
function->otypes = otypes;
|
||||
function->fun = args[0].u_obj;
|
||||
function->type = type;
|
||||
return MP_OBJ_FROM_PTR(function);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(vector_vectorize_obj, 1, vector_vectorize);
|
||||
#endif
|
||||
319
code/numpy/vector.h
Normal file
319
code/numpy/vector.h
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2021 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _VECTOR_
|
||||
#define _VECTOR_
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "../ndarray.h"
|
||||
|
||||
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_acos_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_acosh_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_asin_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_asinh_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_atan_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_atanh_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_ceil_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_cos_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_cosh_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_degrees_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_erf_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_erfc_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_exp_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_expm1_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_floor_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_gamma_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_lgamma_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_log_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_log10_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_log2_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_radians_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_sin_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_sinc_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_sinh_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_tan_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_tanh_obj);
|
||||
#else
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_acos_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_acosh_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_asin_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_asinh_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_atan_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_atanh_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_ceil_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_cos_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_cosh_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_degrees_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_erf_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_erfc_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_exp_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_expm1_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_floor_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_gamma_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_lgamma_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_log_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_log10_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_log2_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_radians_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_sin_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_sinc_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_sinh_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_tan_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_tanh_obj);
|
||||
#endif
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(vector_arctan2_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_around_obj);
|
||||
|
||||
#if ULAB_SUPPORTS_COMPLEX | ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_sqrt_obj);
|
||||
#else
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(vector_sqrt_obj);
|
||||
#endif
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(vector_vectorize_obj);
|
||||
|
||||
typedef struct _vectorized_function_obj_t {
|
||||
mp_obj_base_t base;
|
||||
uint8_t otypes;
|
||||
mp_obj_t fun;
|
||||
const mp_obj_type_t *type;
|
||||
} vectorized_function_obj_t;
|
||||
|
||||
|
||||
#if ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
|
||||
#if ULAB_HAS_FUNCTION_ITERATOR
|
||||
#define ITERATE_VECTOR(type, target, tarray, tstrides, source, sarray)\
|
||||
({\
|
||||
size_t *scoords = ndarray_new_coords((source)->ndim);\
|
||||
for(size_t i = 0; i < (source)->len / (source)->shape[ULAB_MAX_DIMS - 1]; i++) {\
|
||||
for(size_t l = 0; l < (source)->shape[ULAB_MAX_DIMS - 1]; l++) {\
|
||||
*(tarray) = f(*((type *)(sarray)));\
|
||||
(tarray) += (tstrides)[ULAB_MAX_DIMS - 1];\
|
||||
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
|
||||
}\
|
||||
ndarray_rewind_array((source)->ndim, sarray, (source)->shape, (source)->strides, scoords);\
|
||||
}\
|
||||
})
|
||||
|
||||
#else
|
||||
|
||||
#if ULAB_MAX_DIMS == 1
|
||||
#define ITERATE_VECTOR(type, target, tarray, tstrides, source, sarray) do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*(tarray) = f(*((type *)(sarray)));\
|
||||
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
|
||||
(tarray) += (tstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (source)->shape[ULAB_MAX_DIMS-1]);\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 1 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 2
|
||||
#define ITERATE_VECTOR(type, target, tarray, tstrides, source, sarray) do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*(tarray) = f(*((type *)(sarray)));\
|
||||
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
|
||||
(tarray) += (tstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (source)->shape[ULAB_MAX_DIMS-1]);\
|
||||
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\
|
||||
(tarray) -= (tstrides)[ULAB_MAX_DIMS - 1] * (target)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(tarray) += (tstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (source)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 2 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 3
|
||||
#define ITERATE_VECTOR(type, target, tarray, tstrides, source, sarray) do {\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*(tarray) = f(*((type *)(sarray)));\
|
||||
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
|
||||
(tarray) += (tstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (source)->shape[ULAB_MAX_DIMS-1]);\
|
||||
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\
|
||||
(tarray) -= (tstrides)[ULAB_MAX_DIMS - 1] * (target)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(tarray) += (tstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (source)->shape[ULAB_MAX_DIMS-2]);\
|
||||
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(sarray) += (source)->strides[ULAB_MAX_DIMS - 3];\
|
||||
(tarray) -= (tstrides)[ULAB_MAX_DIMS - 2] * (target)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(tarray) += (tstrides)[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (source)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 3 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 4
|
||||
#define ITERATE_VECTOR(type, target, tshape, tstrides, source, sarray) do {\
|
||||
size_t i = 0;\
|
||||
do {\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*(tarray) = f(*((type *)(sarray)));\
|
||||
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
|
||||
(tarray) += (tstrides)[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (source)->shape[ULAB_MAX_DIMS-1]);\
|
||||
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\
|
||||
(tarray) -= (tstrides)[ULAB_MAX_DIMS - 1] * (target)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(tarray) += (tstrides)[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (source)->shape[ULAB_MAX_DIMS-2]);\
|
||||
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(sarray) += (source)->strides[ULAB_MAX_DIMS - 3];\
|
||||
(tarray) -= (tstrides)[ULAB_MAX_DIMS - 2] * (target)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(tarray) += (tstrides)[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (source)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 3] * (source)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(sarray) += (source)->strides[ULAB_MAX_DIMS - 4];\
|
||||
(tarray) -= (tstrides)[ULAB_MAX_DIMS - 3] * (target)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(tarray) += (tstrides)[ULAB_MAX_DIMS - 4];\
|
||||
i++;\
|
||||
} while(i < (source)->shape[ULAB_MAX_DIMS - 4]);\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 4 */
|
||||
#endif /* ULAB_HAS_FUNCTION_ITERATOR */
|
||||
|
||||
#define MATH_FUN_1(py_name, c_name) \
|
||||
static mp_obj_t vector_ ## py_name(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { \
|
||||
return vector_generic_vector(n_args, pos_args, kw_args, MICROPY_FLOAT_C_FUN(c_name)); \
|
||||
}
|
||||
|
||||
#else /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
|
||||
#if ULAB_HAS_FUNCTION_ITERATOR
|
||||
#define ITERATE_VECTOR(type, array, source, sarray, shift)\
|
||||
({\
|
||||
size_t *scoords = ndarray_new_coords((source)->ndim);\
|
||||
for(size_t i=0; i < (source)->len / (source)->shape[ULAB_MAX_DIMS - 1]; i++) {\
|
||||
for(size_t l = 0; l < (source)->shape[ULAB_MAX_DIMS - 1]; l++) {\
|
||||
*(array) = f(*((type *)(sarray)));\
|
||||
(array)++;\
|
||||
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
|
||||
}\
|
||||
ndarray_rewind_array((source)->ndim, sarray, (source)->shape, (source)->strides, scoords);\
|
||||
}\
|
||||
})
|
||||
|
||||
#else
|
||||
|
||||
#if ULAB_MAX_DIMS == 1
|
||||
#define ITERATE_VECTOR(type, array, source, sarray) do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*(array)++ = f(*((type *)(sarray)));\
|
||||
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (source)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 1 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 2
|
||||
#define ITERATE_VECTOR(type, array, source, sarray) do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*(array)++ = f(*((type *)(sarray)));\
|
||||
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (source)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (source)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 2 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 3
|
||||
#define ITERATE_VECTOR(type, array, source, sarray) do {\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*(array)++ = f(*((type *)(sarray)));\
|
||||
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (source)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (source)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(sarray) += (source)->strides[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (source)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 3 */
|
||||
|
||||
#if ULAB_MAX_DIMS == 4
|
||||
#define ITERATE_VECTOR(type, array, source, sarray) do {\
|
||||
size_t i = 0;\
|
||||
do {\
|
||||
size_t j = 0;\
|
||||
do {\
|
||||
size_t k = 0;\
|
||||
do {\
|
||||
size_t l = 0;\
|
||||
do {\
|
||||
*(array)++ = f(*((type *)(sarray)));\
|
||||
(sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
|
||||
l++;\
|
||||
} while(l < (source)->shape[ULAB_MAX_DIMS - 1]);\
|
||||
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS - 1];\
|
||||
(sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\
|
||||
k++;\
|
||||
} while(k < (source)->shape[ULAB_MAX_DIMS - 2]);\
|
||||
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS - 2];\
|
||||
(sarray) += (source)->strides[ULAB_MAX_DIMS - 3];\
|
||||
j++;\
|
||||
} while(j < (source)->shape[ULAB_MAX_DIMS - 3]);\
|
||||
(sarray) -= (source)->strides[ULAB_MAX_DIMS - 3] * (source)->shape[ULAB_MAX_DIMS - 3];\
|
||||
(sarray) += (source)->strides[ULAB_MAX_DIMS - 4];\
|
||||
i++;\
|
||||
} while(i < (source)->shape[ULAB_MAX_DIMS - 4]);\
|
||||
} while(0)
|
||||
#endif /* ULAB_MAX_DIMS == 4 */
|
||||
|
||||
#endif /* ULAB_HAS_FUNCTION_ITERATOR */
|
||||
|
||||
#define MATH_FUN_1(py_name, c_name) \
|
||||
static mp_obj_t vector_ ## py_name(mp_obj_t x_obj) { \
|
||||
return vector_generic_vector(x_obj, MICROPY_FLOAT_C_FUN(c_name)); \
|
||||
}
|
||||
#endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */
|
||||
#endif /* _VECTOR_ */
|
||||
193
code/poly.c
193
code/poly.c
|
|
@ -1,193 +0,0 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/objarray.h"
|
||||
#include "ndarray.h"
|
||||
#include "linalg.h"
|
||||
#include "poly.h"
|
||||
|
||||
|
||||
bool object_is_nditerable(mp_obj_t o_in) {
|
||||
if(mp_obj_is_type(o_in, &ulab_ndarray_type) ||
|
||||
mp_obj_is_type(o_in, &mp_type_tuple) ||
|
||||
mp_obj_is_type(o_in, &mp_type_list) ||
|
||||
mp_obj_is_type(o_in, &mp_type_range)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t get_nditerable_len(mp_obj_t o_in) {
|
||||
if(mp_obj_is_type(o_in, &ulab_ndarray_type)) {
|
||||
ndarray_obj_t *in = MP_OBJ_TO_PTR(o_in);
|
||||
return in->array->len;
|
||||
} else {
|
||||
return (size_t)mp_obj_get_int(mp_obj_len_maybe(o_in));
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) {
|
||||
// TODO: return immediately, if o_p is not an iterable
|
||||
// TODO: there is a bug here: matrices won't work,
|
||||
// because there is a single iteration loop
|
||||
size_t m, n;
|
||||
if(MP_OBJ_IS_TYPE(o_x, &ulab_ndarray_type)) {
|
||||
ndarray_obj_t *ndx = MP_OBJ_TO_PTR(o_x);
|
||||
m = ndx->m;
|
||||
n = ndx->n;
|
||||
} else {
|
||||
mp_obj_array_t *ix = MP_OBJ_TO_PTR(o_x);
|
||||
m = 1;
|
||||
n = ix->len;
|
||||
}
|
||||
// polynomials are going to be of type float, except, when both
|
||||
// the coefficients and the independent variable are integers
|
||||
ndarray_obj_t *out = create_new_ndarray(m, n, NDARRAY_FLOAT);
|
||||
mp_obj_iter_buf_t x_buf;
|
||||
mp_obj_t x_item, x_iterable = mp_getiter(o_x, &x_buf);
|
||||
|
||||
mp_obj_iter_buf_t p_buf;
|
||||
mp_obj_t p_item, p_iterable;
|
||||
|
||||
mp_float_t x, y;
|
||||
mp_float_t *outf = (mp_float_t *)out->array->items;
|
||||
uint8_t plen = mp_obj_get_int(mp_obj_len_maybe(o_p));
|
||||
mp_float_t *p = m_new(mp_float_t, plen);
|
||||
p_iterable = mp_getiter(o_p, &p_buf);
|
||||
uint16_t i = 0;
|
||||
while((p_item = mp_iternext(p_iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
p[i] = mp_obj_get_float(p_item);
|
||||
i++;
|
||||
}
|
||||
i = 0;
|
||||
while ((x_item = mp_iternext(x_iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
x = mp_obj_get_float(x_item);
|
||||
y = p[0];
|
||||
for(uint8_t j=0; j < plen-1; j++) {
|
||||
y *= x;
|
||||
y += p[j+1];
|
||||
}
|
||||
outf[i++] = y;
|
||||
}
|
||||
m_del(mp_float_t, p, plen);
|
||||
return MP_OBJ_FROM_PTR(out);
|
||||
}
|
||||
|
||||
mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) {
|
||||
if((n_args != 2) && (n_args != 3)) {
|
||||
mp_raise_ValueError("number of arguments must be 2, or 3");
|
||||
}
|
||||
if(!object_is_nditerable(args[0])) {
|
||||
mp_raise_ValueError("input data must be an iterable");
|
||||
}
|
||||
uint16_t lenx, leny;
|
||||
uint8_t deg;
|
||||
mp_float_t *x, *XT, *y, *prod;
|
||||
|
||||
if(n_args == 2) { // only the y values are supplied
|
||||
// TODO: this is actually not enough: the first argument can very well be a matrix,
|
||||
// in which case we are between the rock and a hard place
|
||||
leny = (uint16_t)mp_obj_get_int(mp_obj_len_maybe(args[0]));
|
||||
deg = (uint8_t)mp_obj_get_int(args[1]);
|
||||
if(leny < deg) {
|
||||
mp_raise_ValueError("more degrees of freedom than data points");
|
||||
}
|
||||
lenx = leny;
|
||||
x = m_new(mp_float_t, lenx); // assume uniformly spaced data points
|
||||
for(size_t i=0; i < lenx; i++) {
|
||||
x[i] = i;
|
||||
}
|
||||
y = m_new(mp_float_t, leny);
|
||||
fill_array_iterable(y, args[0]);
|
||||
} else if(n_args == 3) {
|
||||
lenx = (uint16_t)mp_obj_get_int(mp_obj_len_maybe(args[0]));
|
||||
leny = (uint16_t)mp_obj_get_int(mp_obj_len_maybe(args[0]));
|
||||
if(lenx != leny) {
|
||||
mp_raise_ValueError("input vectors must be of equal length");
|
||||
}
|
||||
deg = (uint8_t)mp_obj_get_int(args[2]);
|
||||
if(leny < deg) {
|
||||
mp_raise_ValueError("more degrees of freedom than data points");
|
||||
}
|
||||
x = m_new(mp_float_t, lenx);
|
||||
fill_array_iterable(x, args[0]);
|
||||
y = m_new(mp_float_t, leny);
|
||||
fill_array_iterable(y, args[1]);
|
||||
}
|
||||
|
||||
// one could probably express X as a function of XT,
|
||||
// and thereby save RAM, because X is used only in the product
|
||||
XT = m_new(mp_float_t, (deg+1)*leny); // XT is a matrix of shape (deg+1, len) (rows, columns)
|
||||
for(uint8_t i=0; i < leny; i++) { // column index
|
||||
XT[i+0*lenx] = 1.0; // top row
|
||||
for(uint8_t j=1; j < deg+1; j++) { // row index
|
||||
XT[i+j*leny] = XT[i+(j-1)*leny]*x[i];
|
||||
}
|
||||
}
|
||||
|
||||
prod = m_new(mp_float_t, (deg+1)*(deg+1)); // the product matrix is of shape (deg+1, deg+1)
|
||||
mp_float_t sum;
|
||||
for(uint16_t i=0; i < deg+1; i++) { // column index
|
||||
for(uint16_t j=0; j < deg+1; j++) { // row index
|
||||
sum = 0.0;
|
||||
for(size_t k=0; k < lenx; k++) {
|
||||
// (j, k) * (k, i)
|
||||
// Note that the second matrix is simply the transpose of the first:
|
||||
// X(k, i) = XT(i, k) = XT[k*lenx+i]
|
||||
sum += XT[j*lenx+k]*XT[i*lenx+k]; // X[k*(deg+1)+i];
|
||||
}
|
||||
prod[j*(deg+1)+i] = sum;
|
||||
}
|
||||
}
|
||||
if(!linalg_invert_matrix(prod, deg+1)) {
|
||||
// Although X was a Vandermonde matrix, whose inverse is guaranteed to exist,
|
||||
// we bail out here, if prod couldn't be inverted: if the values in x are not all
|
||||
// distinct, prod is singular
|
||||
m_del(mp_float_t, XT, (deg+1)*lenx);
|
||||
m_del(mp_float_t, x, lenx);
|
||||
m_del(mp_float_t, y, lenx);
|
||||
m_del(mp_float_t, prod, (deg+1)*(deg+1));
|
||||
mp_raise_ValueError("could not invert Vandermonde matrix");
|
||||
}
|
||||
// at this point, we have the inverse of X^T * X
|
||||
// y is a column vector; x is free now, we can use it for storing intermediate values
|
||||
for(uint16_t i=0; i < deg+1; i++) { // row index
|
||||
sum = 0.0;
|
||||
for(uint16_t j=0; j < lenx; j++) { // column index
|
||||
sum += XT[i*lenx+j]*y[j];
|
||||
}
|
||||
x[i] = sum;
|
||||
}
|
||||
// XT is no longer needed
|
||||
m_del(mp_float_t, XT, (deg+1)*leny);
|
||||
|
||||
ndarray_obj_t *beta = create_new_ndarray(deg+1, 1, NDARRAY_FLOAT);
|
||||
mp_float_t *betav = (mp_float_t *)beta->array->items;
|
||||
// x[0..(deg+1)] contains now the product X^T * y; we can get rid of y
|
||||
m_del(float, y, leny);
|
||||
|
||||
// now, we calculate beta, i.e., we apply prod = (X^T * X)^(-1) on x = X^T * y; x is a column vector now
|
||||
for(uint16_t i=0; i < deg+1; i++) {
|
||||
sum = 0.0;
|
||||
for(uint16_t j=0; j < deg+1; j++) {
|
||||
sum += prod[i*(deg+1)+j]*x[j];
|
||||
}
|
||||
betav[i] = sum;
|
||||
}
|
||||
m_del(mp_float_t, x, lenx);
|
||||
m_del(mp_float_t, prod, (deg+1)*(deg+1));
|
||||
for(uint8_t i=0; i < (deg+1)/2; i++) {
|
||||
// We have to reverse the array, for the leading coefficient comes first.
|
||||
SWAP(mp_float_t, betav[i], betav[deg-i]);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(beta);
|
||||
}
|
||||
17
code/poly.h
17
code/poly.h
|
|
@ -1,17 +0,0 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _POLY_
|
||||
#define _POLY_
|
||||
|
||||
mp_obj_t poly_polyval(mp_obj_t , mp_obj_t );
|
||||
mp_obj_t poly_polyfit(size_t , const mp_obj_t *);
|
||||
|
||||
#endif
|
||||
701
code/scipy/integrate/integrate.c
Normal file
701
code/scipy/integrate/integrate.c
Normal file
|
|
@ -0,0 +1,701 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2024 Harald Milz <hm@seneca.muc.de>
|
||||
*
|
||||
* References:
|
||||
* - Dr. Robert van Engelen, Improving the mp_float_t Exponential Quadrature Tanh-Sinh, Sinh-Sinh and Exp-Sinh Formulas,
|
||||
* 2021, https://www.genivia.com/qthsh.html
|
||||
* - Borwein, Bailey & Girgensohn, "Experimentation in Mathematics - Computational Paths to Discovery", A K Peters,
|
||||
* 2003, pages 312-313
|
||||
* - Joren Vanherck, Bart Sorée, Wim Magnus, Tanh-sinh quadrature for single and multiple integration using
|
||||
* floating-point arithmetic, 2020, https://arxiv.org/abs/2007.15057
|
||||
* - Tanh-Sinh quadrature, Wikipedia, https://en.wikipedia.org/wiki/Tanh-sinh_quadrature
|
||||
* - Romberg's method, Wikipedia, https://en.wikipedia.org/wiki/Romberg%27s_method
|
||||
* - Adaptive Simpson's method, Wikipedia, https://en.wikipedia.org/wiki/Adaptive_Simpson%27s_method
|
||||
* - Gauss–Kronrod quadrature formula, Wikipedia, https://en.wikipedia.org/wiki/Gauss%E2%80%93Kronrod_quadrature_formula
|
||||
*
|
||||
* This module provides four integration methods, and thus deviates from scipy.integrate a bit.
|
||||
* As for the pros and cons of the different methods please consult the literature above.
|
||||
* The code was ported to Micropython from Dr. Engelen's paper and used with his written kind permission
|
||||
* - quad - Tanh-Sinh, Sinh-Sinh and Exp-Sinh quadrature
|
||||
* - romberg - Romberg quadrature
|
||||
* - simpson - Adaptive Simpson quadrature
|
||||
* - quadgk - Adaptive Gauss-Kronrod (G10,K21) quadrature
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
#include "py/objtuple.h"
|
||||
|
||||
#include "../../ndarray.h"
|
||||
#include "../../ulab.h"
|
||||
#include "../../ulab_tools.h"
|
||||
#include "integrate.h"
|
||||
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
|
||||
ULAB_DEFINE_FLOAT_CONST(etolerance, MICROPY_FLOAT_CONST(1e-14), 0x283424dcUL, 0x3e901b2b29a4692bULL);
|
||||
#define ULAB_MACHEPS MICROPY_FLOAT_CONST(1e-17)
|
||||
#else
|
||||
ULAB_DEFINE_FLOAT_CONST(etolerance, MICROPY_FLOAT_CONST(1e-8), 0x358637cfUL, 0x3e7010c6f7d42d18ULL);
|
||||
#define ULAB_MACHEPS MICROPY_FLOAT_CONST(1e-8)
|
||||
#endif
|
||||
|
||||
#define ULAB_ZERO MICROPY_FLOAT_CONST(0.0)
|
||||
#define ULAB_POINT_TWO_FIVE MICROPY_FLOAT_CONST(0.25)
|
||||
#define ULAB_ONE MICROPY_FLOAT_CONST(1.0)
|
||||
#define ULAB_TWO MICROPY_FLOAT_CONST(2.0)
|
||||
#define ULAB_FOUR MICROPY_FLOAT_CONST(4.0)
|
||||
#define ULAB_SIX MICROPY_FLOAT_CONST(6.0)
|
||||
#define ULAB_TEN MICROPY_FLOAT_CONST(10.0)
|
||||
#define ULAB_FIFTEEN MICROPY_FLOAT_CONST(15.0)
|
||||
#define ULAB_EPSILON_5 MICROPY_FLOAT_CONST(1e-5)
|
||||
|
||||
|
||||
static mp_float_t integrate_python_call(const mp_obj_type_t *type, mp_obj_t fun, mp_float_t x, mp_obj_t *fargs, uint8_t nparams) {
|
||||
// Helper function for calculating the value of f(x, a, b, c, ...),
|
||||
// where f is defined in python. Takes a float, returns a float.
|
||||
// The array of mp_obj_t type must be supplied, as must the number of parameters (a, b, c...) in nparams
|
||||
fargs[0] = mp_obj_new_float(x);
|
||||
return mp_obj_get_float(MP_OBJ_TYPE_GET_SLOT(type, call)(fun, nparams+1, 0, fargs));
|
||||
}
|
||||
|
||||
// sign helper function
|
||||
int sign(mp_float_t x) {
|
||||
if (x >= ULAB_ZERO)
|
||||
return 1;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
#if ULAB_INTEGRATE_HAS_TANHSINH
|
||||
// Tanh-Sinh, Sinh-Sinh and Exp-Sinh quadrature
|
||||
// https://www.genivia.com/qthsh.html
|
||||
|
||||
// return optimized Exp-Sinh integral split point d
|
||||
mp_float_t exp_sinh_opt_d(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t eps, mp_float_t d) {
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
mp_obj_t fargs[1];
|
||||
mp_float_t h2 = integrate_python_call(type, fun, a + d/2, fargs, 0) - integrate_python_call(type, fun, (a + d*2)*4, fargs, 0);
|
||||
int i = 1, j = 32; // j=32 is optimal to find r
|
||||
if (isfinite(h2) && MICROPY_FLOAT_C_FUN(fabs)(h2) > ULAB_EPSILON_5) { // if |h2| > 2^-16
|
||||
mp_float_t r, fl, fr, h, s = 0, lfl, lfr, lr = 2;
|
||||
do { // find max j such that fl and fr are finite
|
||||
j /= 2;
|
||||
r = 1 << (i + j);
|
||||
fl = integrate_python_call(type, fun, a + d/r, fargs, 0);
|
||||
fr = integrate_python_call(type, fun, (a + d*r)*r*r, fargs, 0);
|
||||
h = fl - fr;
|
||||
} while (j > 1 && !isfinite(h));
|
||||
if (j > 1 && isfinite(h) && sign(h) != sign(h2)) {
|
||||
lfl = fl; // last fl=f(a+d/r)
|
||||
lfr = fr; // last fr=f(a+d*r)*r*r
|
||||
do { // bisect in 4 iterations
|
||||
j /= 2;
|
||||
r = 1 << (i + j);
|
||||
fl = integrate_python_call(type, fun, a + d/r, fargs, 0);
|
||||
fr = integrate_python_call(type, fun, (a + d*r)*r*r, fargs, 0);
|
||||
h = fl - fr;
|
||||
if (isfinite(h)) {
|
||||
s += MICROPY_FLOAT_C_FUN(fabs)(h); // sum |h| to remove noisy cases
|
||||
if (sign(h) == sign(h2)) {
|
||||
i += j; // search right half
|
||||
}
|
||||
else { // search left half
|
||||
lfl = fl; // record last fl=f(a+d/r)
|
||||
lfr = fr; // record last fl=f(a+d*r)*r*r
|
||||
lr = r; // record last r
|
||||
}
|
||||
}
|
||||
} while (j > 1);
|
||||
if (s > eps) { // if sum of |h| > eps
|
||||
h = lfl - lfr; // use last fl and fr before the sign change
|
||||
r = lr; // use last r before the sign change
|
||||
if (h != ULAB_ZERO) // if last diff != 0, back up r by one step
|
||||
r /= ULAB_TWO;
|
||||
if (MICROPY_FLOAT_C_FUN(fabs)(lfl) < MICROPY_FLOAT_C_FUN(fabs)(lfr))
|
||||
d /= r; // move d closer to the finite endpoint
|
||||
else
|
||||
d *= r; // move d closer to the infinite endpoint
|
||||
}
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
// integrate function f, range a..b, max levels n, error tolerance eps
|
||||
mp_float_t tanhsinh(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, uint16_t n, mp_float_t eps, mp_float_t *e) {
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
mp_obj_t fargs[1];
|
||||
const mp_float_t tol = ULAB_TEN * eps;
|
||||
mp_float_t c = ULAB_ZERO, d = ULAB_ONE, s, sign = ULAB_ONE, v, h = ULAB_TWO;
|
||||
int k = 0, mode = 0; // Tanh-Sinh = 0, Exp-Sinh = 1, Sinh-Sinh = 2
|
||||
if (b < a) { // swap bounds
|
||||
v = b;
|
||||
b = a;
|
||||
a = v;
|
||||
sign = -1;
|
||||
}
|
||||
if (isfinite(a) && isfinite(b)) {
|
||||
c = (a+b) / ULAB_TWO;
|
||||
d = (b-a) / ULAB_TWO;
|
||||
v = c;
|
||||
}
|
||||
else if (isfinite(a)) {
|
||||
mode = 1; // Exp-Sinh
|
||||
d = exp_sinh_opt_d(fun, a, eps, d);
|
||||
c = a;
|
||||
v = a+d;
|
||||
}
|
||||
else if (isfinite(b)) {
|
||||
mode = 1; // Exp-Sinh
|
||||
// d = -d;
|
||||
d = exp_sinh_opt_d(fun, b, eps, -d);
|
||||
sign = -sign;
|
||||
c = b;
|
||||
v = b+d;
|
||||
}
|
||||
else {
|
||||
mode = 2; // Sinh-Sinh
|
||||
v = ULAB_ZERO;
|
||||
}
|
||||
s = integrate_python_call(type, fun, v, fargs, 0);
|
||||
do {
|
||||
mp_float_t p = ULAB_ZERO, q, fp = ULAB_ZERO, fm = ULAB_ZERO, t, eh;
|
||||
h /= ULAB_TWO;
|
||||
t = eh = MICROPY_FLOAT_C_FUN(exp)(h);
|
||||
if (k > ULAB_ZERO)
|
||||
eh *= eh;
|
||||
if (mode == 0) { // Tanh-Sinh
|
||||
do {
|
||||
mp_float_t u = MICROPY_FLOAT_C_FUN(exp)(ULAB_ONE / t - t); // = exp(-2*sinh(j*h)) = 1/exp(sinh(j*h))^2
|
||||
mp_float_t r = ULAB_TWO * u / (ULAB_ONE + u); // = 1 - tanh(sinh(j*h))
|
||||
mp_float_t w = (t + ULAB_ONE / t) * r / (ULAB_ONE + u); // = cosh(j*h)/cosh(sinh(j*h))^2
|
||||
mp_float_t x = d*r;
|
||||
if (a+x > a) { // if too close to a then reuse previous fp
|
||||
mp_float_t y = integrate_python_call(type, fun, a+x, fargs, 0);
|
||||
if (isfinite(y))
|
||||
fp = y; // if f(x) is finite, add to local sum
|
||||
}
|
||||
if (b-x < b) { // if too close to a then reuse previous fp
|
||||
mp_float_t y = integrate_python_call(type, fun, b-x, fargs, 0);
|
||||
if (isfinite(y))
|
||||
fm = y; // if f(x) is finite, add to local sum
|
||||
}
|
||||
q = w*(fp+fm);
|
||||
p += q;
|
||||
t *= eh;
|
||||
} while (MICROPY_FLOAT_C_FUN(fabs)(q) > eps*MICROPY_FLOAT_C_FUN(fabs)(p));
|
||||
}
|
||||
else {
|
||||
t /= ULAB_TWO;
|
||||
do {
|
||||
mp_float_t r = MICROPY_FLOAT_C_FUN(exp)(t - ULAB_POINT_TWO_FIVE / t); // = exp(sinh(j*h))
|
||||
mp_float_t x, y, w = r;
|
||||
q = ULAB_ZERO;
|
||||
if (mode == 1) { // Exp-Sinh
|
||||
x = c + d/r;
|
||||
if (x == c) // if x hit the finite endpoint then break
|
||||
break;
|
||||
y = integrate_python_call(type, fun, x, fargs, 0);
|
||||
if (isfinite(y)) // if f(x) is finite, add to local sum
|
||||
q += y/w;
|
||||
}
|
||||
else { // Sinh-Sinh
|
||||
r = (r - ULAB_ONE / r) / ULAB_TWO; // = sinh(sinh(j*h))
|
||||
w = (w + ULAB_ONE / w) / ULAB_TWO; // = cosh(sinh(j*h))
|
||||
x = c - d*r;
|
||||
y = integrate_python_call(type, fun, x, fargs, 0);
|
||||
if (isfinite(y)) // if f(x) is finite, add to local sum
|
||||
q += y*w;
|
||||
}
|
||||
x = c + d*r;
|
||||
y = integrate_python_call(type, fun, x, fargs, 0);
|
||||
if (isfinite(y)) // if f(x) is finite, add to local sum
|
||||
q += y*w;
|
||||
q *= t + ULAB_POINT_TWO_FIVE / t; // q *= cosh(j*h)
|
||||
p += q;
|
||||
t *= eh;
|
||||
} while (MICROPY_FLOAT_C_FUN(fabs)(q) > eps*MICROPY_FLOAT_C_FUN(fabs)(p));
|
||||
}
|
||||
v = s-p;
|
||||
s += p;
|
||||
++k;
|
||||
} while (MICROPY_FLOAT_C_FUN(fabs)(v) > tol*MICROPY_FLOAT_C_FUN(fabs)(s) && k <= n);
|
||||
// return the error estimate by reference
|
||||
*e = MICROPY_FLOAT_C_FUN(fabs)(v)/(MICROPY_FLOAT_C_FUN(fabs)(s)+eps);
|
||||
return sign*d*s*h; // result with estimated relative error e
|
||||
}
|
||||
|
||||
//| def tanhsinh(
|
||||
//| fun: Callable[[float], float],
|
||||
//| a: float,
|
||||
//| b: float,
|
||||
//| *,
|
||||
//| levels: int = 6
|
||||
//| eps: float = etolerance
|
||||
//| ) -> float:
|
||||
//| """
|
||||
//| :param callable f: The function to integrate
|
||||
//| :param float a: The lower integration limit
|
||||
//| :param float b: The upper integration limit
|
||||
//| :param float levels: The number of levels to perform (6..7 is optimal)
|
||||
//| :param float eps: The error tolerance value
|
||||
//|
|
||||
//| Find a quadrature of the function ``f(x)`` on the interval
|
||||
//| (``a``..``b``) using an optimized double exponential. The result is accurate to within
|
||||
//| ``eps`` unless more than ``levels`` levels are required."""
|
||||
//|
|
||||
|
||||
|
||||
static mp_obj_t integrate_tanhsinh(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_levels, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 6} },
|
||||
{ MP_QSTR_eps, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(etolerance)} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
mp_obj_t fun = args[0].u_obj;
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a callable"));
|
||||
}
|
||||
|
||||
// iterate over args 1, 2, and 4
|
||||
// arg 3 will be handled by MP_ARG_INT above.
|
||||
for (int i=1; i<=4; i*=2) {
|
||||
type = mp_obj_get_type(args[i].u_obj);
|
||||
if (type != &mp_type_float && type != &mp_type_int) {
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("can't convert arg %d from %s to float"), i, mp_obj_get_type_str(args[i].u_obj));
|
||||
}
|
||||
}
|
||||
mp_float_t a = mp_obj_get_float(args[1].u_obj);
|
||||
mp_float_t b = mp_obj_get_float(args[2].u_obj);
|
||||
uint16_t n = (uint16_t)args[3].u_int;
|
||||
if (n < 1) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("levels needs to be a positive integer"));
|
||||
}
|
||||
mp_float_t eps = mp_obj_get_float(args[4].u_obj);
|
||||
|
||||
mp_obj_t res[2];
|
||||
mp_float_t e;
|
||||
res[0] = mp_obj_new_float(tanhsinh(fun, a, b, n, eps, &e));
|
||||
res[1] = mp_obj_new_float(e);
|
||||
return mp_obj_new_tuple(2, res);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(integrate_tanhsinh_obj, 2, integrate_tanhsinh);
|
||||
#endif /* ULAB_INTEGRATE_HAS_TANHSINH */
|
||||
|
||||
#if ULAB_INTEGRATE_HAS_ROMBERG
|
||||
// Romberg quadrature
|
||||
// This function is deprecated as of SciPy 1.12.0 and will be removed in SciPy 1.15.0. Please use scipy.integrate.quad instead.
|
||||
// https://en.wikipedia.org/wiki/Romberg%27s_method, https://www.genivia.com/qthsh.html,
|
||||
// https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.romberg.html (which is different
|
||||
// insofar as the latter expects an array of function values).
|
||||
|
||||
mp_float_t qromb(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, uint16_t n, mp_float_t eps) {
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
mp_obj_t fargs[1];
|
||||
mp_float_t R1[n], R2[n];
|
||||
mp_float_t *Ro = &R1[0], *Ru = &R2[0];
|
||||
mp_float_t h = b-a;
|
||||
uint16_t i, j;
|
||||
Ro[0] = (integrate_python_call(type, fun, a, fargs, 0) + integrate_python_call(type, fun, b, fargs, 0)) * h/2;
|
||||
for (i = 1; i < n; ++i) {
|
||||
unsigned long long k = 1UL << i;
|
||||
unsigned long long s = 1;
|
||||
mp_float_t sum = ULAB_ZERO;
|
||||
mp_float_t *Rt;
|
||||
h /= ULAB_TWO;
|
||||
for (j = 1; j < k; j += 2)
|
||||
sum += integrate_python_call(type, fun, a+j*h, fargs, 0);
|
||||
Ru[0] = h*sum + Ro[0] / ULAB_TWO;
|
||||
for (j = 1; j <= i; ++j) {
|
||||
s <<= 2;
|
||||
Ru[j] = (s*Ru[j-1] - Ro[j-1])/(s-1);
|
||||
}
|
||||
if (i > 2 && MICROPY_FLOAT_C_FUN(fabs)(Ro[i-1]-Ru[i]) <= eps*MICROPY_FLOAT_C_FUN(fabs)(Ru[i])+eps)
|
||||
return Ru[i];
|
||||
Rt = Ro;
|
||||
Ro = Ru;
|
||||
Ru = Rt;
|
||||
}
|
||||
return Ro[n-1];
|
||||
}
|
||||
|
||||
//| def romberg(
|
||||
//| fun: Callable[[float], float],
|
||||
//| a: float,
|
||||
//| b: float,
|
||||
//| *,
|
||||
//| steps: int = 100
|
||||
//| eps: float = etolerance
|
||||
//| ) -> float:
|
||||
//| """
|
||||
//| :param callable f: The function to integrate
|
||||
//| :param float a: The lower integration limit
|
||||
//| :param float b: The upper integration limit
|
||||
//| :param float steps: The number of equidistant steps
|
||||
//| :param float eps: The tolerance value
|
||||
//|
|
||||
//| Find a quadrature of the function ``f(x)`` on the interval
|
||||
//| (``a``..``b``) using the Romberg method. The result is accurate to within
|
||||
//| ``eps`` unless more than ``steps`` steps are required."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t integrate_romberg(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_steps, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 100} },
|
||||
{ MP_QSTR_eps, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(etolerance)} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
mp_obj_t fun = args[0].u_obj;
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a callable"));
|
||||
}
|
||||
|
||||
// iterate over args 1, 2, and 4
|
||||
// arg 3 will be handled by MP_ARG_INT above.
|
||||
for (int i=1; i<=4; i*=2) {
|
||||
type = mp_obj_get_type(args[i].u_obj);
|
||||
if (type != &mp_type_float && type != &mp_type_int) {
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("can't convert arg %d from %s to float"), i, mp_obj_get_type_str(args[i].u_obj));
|
||||
}
|
||||
}
|
||||
mp_float_t a = mp_obj_get_float(args[1].u_obj);
|
||||
mp_float_t b = mp_obj_get_float(args[2].u_obj);
|
||||
uint16_t steps = (uint16_t)args[3].u_int;
|
||||
if (steps < 1) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("steps needs to be a positive integer"));
|
||||
}
|
||||
mp_float_t eps = mp_obj_get_float(args[4].u_obj);
|
||||
|
||||
return mp_obj_new_float(qromb(fun, a, b, steps, eps));
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(integrate_romberg_obj, 2, integrate_romberg);
|
||||
#endif /* ULAB_INTEGRATE_HAS_ROMBERG */
|
||||
|
||||
#if ULAB_INTEGRATE_HAS_SIMPSON
|
||||
// Adaptive Simpson quadrature
|
||||
// https://en.wikipedia.org/wiki/Adaptive_Simpson%27s_method, https://www.genivia.com/qthsh.html
|
||||
|
||||
mp_float_t as(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, mp_float_t fa, mp_float_t fm,
|
||||
mp_float_t fb, mp_float_t v, mp_float_t eps, int n, mp_float_t t) {
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
mp_obj_t fargs[1];
|
||||
mp_float_t h = (b-a) / ULAB_TWO;
|
||||
mp_float_t f1 = integrate_python_call(type, fun, a + h / ULAB_TWO, fargs, 0);
|
||||
mp_float_t f2 = integrate_python_call(type, fun, b - h / ULAB_TWO, fargs, 0);
|
||||
mp_float_t sl = h*(fa + ULAB_FOUR * f1 + fm) / ULAB_SIX;
|
||||
mp_float_t sr = h*(fm + ULAB_FOUR * f2 + fb) / ULAB_SIX;
|
||||
mp_float_t s = sl+sr;
|
||||
mp_float_t d = (s-v) / ULAB_FIFTEEN;
|
||||
mp_float_t m = a+h;
|
||||
if (n <= 0 || MICROPY_FLOAT_C_FUN(fabs)(d) < eps)
|
||||
return t + s + d; // note: fabs(d) can be used as error estimate
|
||||
eps /= ULAB_TWO;
|
||||
--n;
|
||||
t = as(fun, a, m, fa, f1, fm, sl, eps, n, t);
|
||||
return as(fun, m, b, fm, f2, fb, sr, eps, n, t);
|
||||
}
|
||||
|
||||
mp_float_t qasi(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, int n, mp_float_t eps) {
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
mp_obj_t fargs[1];
|
||||
mp_float_t fa = integrate_python_call(type, fun, a, fargs, 0);
|
||||
mp_float_t fm = integrate_python_call(type, fun, (a+b)/2, fargs, 0);
|
||||
mp_float_t fb = integrate_python_call(type, fun, b, fargs, 0);
|
||||
mp_float_t v = (fa + ULAB_FOUR * fm + fb) * (b-a) / ULAB_SIX;
|
||||
return as(fun, a, b, fa, fm, fb, v, eps, n, 0);
|
||||
}
|
||||
|
||||
//| def simpson(
|
||||
//| fun: Callable[[float], float],
|
||||
//| a: float,
|
||||
//| b: float,
|
||||
//| *,
|
||||
//| steps: int = 100
|
||||
//| eps: float = etolerance
|
||||
//| ) -> float:
|
||||
//| """
|
||||
//| :param callable f: The function to integrate
|
||||
//| :param float a: The lower integration limit
|
||||
//| :param float b: The upper integration limit
|
||||
//| :param float steps: The number of equidistant steps
|
||||
//| :param float eps: The tolerance value
|
||||
//|
|
||||
//| Find a quadrature of the function ``f(x)`` on the interval
|
||||
//| (``a``..``b``) using the Adaptive Simpson's method. The result is accurate to within
|
||||
//| ``eps`` unless more than ``steps`` steps are required."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t integrate_simpson(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_steps, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 100} },
|
||||
{ MP_QSTR_eps, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(etolerance)} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
mp_obj_t fun = args[0].u_obj;
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a function"));
|
||||
}
|
||||
|
||||
// iterate over args 1, 2, and 4
|
||||
// arg 3 will be handled by MP_ARG_INT above.
|
||||
for (int i=1; i<=4; i*=2) {
|
||||
type = mp_obj_get_type(args[i].u_obj);
|
||||
if (type != &mp_type_float && type != &mp_type_int) {
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("can't convert arg %d from %s to float"), i, mp_obj_get_type_str(args[i].u_obj));
|
||||
}
|
||||
}
|
||||
mp_float_t a = mp_obj_get_float(args[1].u_obj);
|
||||
mp_float_t b = mp_obj_get_float(args[2].u_obj);
|
||||
uint16_t steps = (uint16_t)args[3].u_int;
|
||||
if (steps < 1) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("steps needs to be a positive integer"));
|
||||
}
|
||||
mp_float_t eps = mp_obj_get_float(args[4].u_obj);
|
||||
|
||||
return mp_obj_new_float(qasi(fun, a, b, steps, eps));
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(integrate_simpson_obj, 2, integrate_simpson);
|
||||
#endif /* ULAB_INTEGRATE_HAS_SIMPSON */
|
||||
|
||||
#if ULAB_INTEGRATE_HAS_QUAD
|
||||
// Adaptive Gauss-Kronrod (G10,K21) quadrature
|
||||
// https://en.wikipedia.org/wiki/Gauss%E2%80%93Kronrod_quadrature_formula, https://www.genivia.com/qthsh.html
|
||||
|
||||
mp_float_t gk(mp_float_t (*fun)(mp_float_t), mp_float_t c, mp_float_t d, mp_float_t *err) {
|
||||
// abscissas and weights pre-calculated with Legendre Stieltjes polynomials
|
||||
static const mp_float_t abscissas[21] = {
|
||||
MICROPY_FLOAT_CONST(0.00000000000000000e+00),
|
||||
MICROPY_FLOAT_CONST(7.65265211334973338e-02),
|
||||
MICROPY_FLOAT_CONST(1.52605465240922676e-01),
|
||||
MICROPY_FLOAT_CONST(2.27785851141645078e-01),
|
||||
MICROPY_FLOAT_CONST(3.01627868114913004e-01),
|
||||
MICROPY_FLOAT_CONST(3.73706088715419561e-01),
|
||||
MICROPY_FLOAT_CONST(4.43593175238725103e-01),
|
||||
MICROPY_FLOAT_CONST(5.10867001950827098e-01),
|
||||
MICROPY_FLOAT_CONST(5.75140446819710315e-01),
|
||||
MICROPY_FLOAT_CONST(6.36053680726515025e-01),
|
||||
MICROPY_FLOAT_CONST(6.93237656334751385e-01),
|
||||
MICROPY_FLOAT_CONST(7.46331906460150793e-01),
|
||||
MICROPY_FLOAT_CONST(7.95041428837551198e-01),
|
||||
MICROPY_FLOAT_CONST(8.39116971822218823e-01),
|
||||
MICROPY_FLOAT_CONST(8.78276811252281976e-01),
|
||||
MICROPY_FLOAT_CONST(9.12234428251325906e-01),
|
||||
MICROPY_FLOAT_CONST(9.40822633831754754e-01),
|
||||
MICROPY_FLOAT_CONST(9.63971927277913791e-01),
|
||||
MICROPY_FLOAT_CONST(9.81507877450250259e-01),
|
||||
MICROPY_FLOAT_CONST(9.93128599185094925e-01),
|
||||
MICROPY_FLOAT_CONST(9.98859031588277664e-01),
|
||||
};
|
||||
static const mp_float_t weights[21] = {
|
||||
MICROPY_FLOAT_CONST(7.66007119179996564e-02),
|
||||
MICROPY_FLOAT_CONST(7.63778676720807367e-02),
|
||||
MICROPY_FLOAT_CONST(7.57044976845566747e-02),
|
||||
MICROPY_FLOAT_CONST(7.45828754004991890e-02),
|
||||
MICROPY_FLOAT_CONST(7.30306903327866675e-02),
|
||||
MICROPY_FLOAT_CONST(7.10544235534440683e-02),
|
||||
MICROPY_FLOAT_CONST(6.86486729285216193e-02),
|
||||
MICROPY_FLOAT_CONST(6.58345971336184221e-02),
|
||||
MICROPY_FLOAT_CONST(6.26532375547811680e-02),
|
||||
MICROPY_FLOAT_CONST(5.91114008806395724e-02),
|
||||
MICROPY_FLOAT_CONST(5.51951053482859947e-02),
|
||||
MICROPY_FLOAT_CONST(5.09445739237286919e-02),
|
||||
MICROPY_FLOAT_CONST(4.64348218674976747e-02),
|
||||
MICROPY_FLOAT_CONST(4.16688733279736863e-02),
|
||||
MICROPY_FLOAT_CONST(3.66001697582007980e-02),
|
||||
MICROPY_FLOAT_CONST(3.12873067770327990e-02),
|
||||
MICROPY_FLOAT_CONST(2.58821336049511588e-02),
|
||||
MICROPY_FLOAT_CONST(2.03883734612665236e-02),
|
||||
MICROPY_FLOAT_CONST(1.46261692569712530e-02),
|
||||
MICROPY_FLOAT_CONST(8.60026985564294220e-03),
|
||||
MICROPY_FLOAT_CONST(3.07358371852053150e-03),
|
||||
};
|
||||
static const mp_float_t gauss_weights[10] = {
|
||||
MICROPY_FLOAT_CONST(1.52753387130725851e-01),
|
||||
MICROPY_FLOAT_CONST(1.49172986472603747e-01),
|
||||
MICROPY_FLOAT_CONST(1.42096109318382051e-01),
|
||||
MICROPY_FLOAT_CONST(1.31688638449176627e-01),
|
||||
MICROPY_FLOAT_CONST(1.18194531961518417e-01),
|
||||
MICROPY_FLOAT_CONST(1.01930119817240435e-01),
|
||||
MICROPY_FLOAT_CONST(8.32767415767047487e-02),
|
||||
MICROPY_FLOAT_CONST(6.26720483341090636e-02),
|
||||
MICROPY_FLOAT_CONST(4.06014298003869413e-02),
|
||||
MICROPY_FLOAT_CONST(1.76140071391521183e-02),
|
||||
};
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
mp_obj_t fargs[1];
|
||||
mp_float_t p = ULAB_ZERO; // kronrod quadrature sum
|
||||
mp_float_t q = ULAB_ZERO; // gauss quadrature sum
|
||||
mp_float_t fp, fm;
|
||||
mp_float_t e;
|
||||
int i;
|
||||
fp = integrate_python_call(type, fun, c, fargs, 0);
|
||||
p = fp * weights[0];
|
||||
for (i = 1; i < 21; i += 2) {
|
||||
fp = integrate_python_call(type, fun, c + d * abscissas[i], fargs, 0);
|
||||
fm = integrate_python_call(type, fun, c - d * abscissas[i], fargs, 0);
|
||||
p += (fp + fm) * weights[i];
|
||||
q += (fp + fm) * gauss_weights[i/2];
|
||||
}
|
||||
for (i = 2; i < 21; i += 2) {
|
||||
fp = integrate_python_call(type, fun, c + d * abscissas[i], fargs, 0);
|
||||
fm = integrate_python_call(type, fun, c - d * abscissas[i], fargs, 0);
|
||||
p += (fp + fm) * weights[i];
|
||||
}
|
||||
*err = MICROPY_FLOAT_C_FUN(fabs)(p - q);
|
||||
e = MICROPY_FLOAT_C_FUN(fabs)(2 * p * ULAB_MACHEPS); // optional, to take 1e-17 MachEps prec. into account
|
||||
if (*err < e)
|
||||
*err = e;
|
||||
return p;
|
||||
}
|
||||
|
||||
mp_float_t qakro(mp_float_t (*fun)(mp_float_t), mp_float_t a, mp_float_t b, int n, mp_float_t tol, mp_float_t eps, mp_float_t *err) {
|
||||
mp_float_t c = (a+b) / ULAB_TWO;
|
||||
mp_float_t d = (b-a) / ULAB_TWO;
|
||||
mp_float_t e;
|
||||
mp_float_t r = gk(fun, c, d, &e);
|
||||
mp_float_t s = d*r;
|
||||
mp_float_t t = MICROPY_FLOAT_C_FUN(fabs)(s*tol);
|
||||
if (tol == ULAB_ZERO)
|
||||
tol = t;
|
||||
if (n > 0 && t < e && tol < e) {
|
||||
s = qakro(fun, a, c, n-1, t / ULAB_TWO, eps, err);
|
||||
s += qakro(fun, c, b, n-1, t / ULAB_TWO, eps, &e);
|
||||
*err += e;
|
||||
return s;
|
||||
}
|
||||
*err = e;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
//| def quad(
|
||||
//| fun: Callable[[float], float],
|
||||
//| a: float,
|
||||
//| b: float,
|
||||
//| *,
|
||||
//| order: int = 5
|
||||
//| eps: float = etolerance
|
||||
//| ) -> float:
|
||||
//| """
|
||||
//| :param callable f: The function to integrate
|
||||
//| :param float a: The lower integration limit
|
||||
//| :param float b: The upper integration limit
|
||||
//| :param float order: Order of quadrature integration. Default is 5.
|
||||
//| :param float eps: The tolerance value
|
||||
//|
|
||||
//| Find a quadrature of the function ``f(x)`` on the interval
|
||||
//| (``a``..``b``) using the Adaptive Gauss-Kronrod method. The result is accurate to within
|
||||
//| ``eps`` unless a higher order than ``order`` is required."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t integrate_quad(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_order, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5} },
|
||||
{ MP_QSTR_eps, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(etolerance)} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
mp_obj_t fun = args[0].u_obj;
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a callable"));
|
||||
}
|
||||
|
||||
// iterate over args 1, 2, and 4
|
||||
// arg 3 will be handled by MP_ARG_INT above.
|
||||
for (int i=1; i<=4; i*=2) {
|
||||
type = mp_obj_get_type(args[i].u_obj);
|
||||
if (type != &mp_type_float && type != &mp_type_int) {
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("can't convert arg %d from %s to float"), i, mp_obj_get_type_str(args[i].u_obj));
|
||||
}
|
||||
}
|
||||
mp_float_t a = mp_obj_get_float(args[1].u_obj);
|
||||
mp_float_t b = mp_obj_get_float(args[2].u_obj);
|
||||
uint16_t order = (uint16_t)args[3].u_int;
|
||||
if (order < 1) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("order needs to be a positive integer"));
|
||||
}
|
||||
mp_float_t eps = mp_obj_get_float(args[4].u_obj);
|
||||
|
||||
mp_obj_t res[2];
|
||||
mp_float_t e;
|
||||
res[0] = mp_obj_new_float(qakro(fun, a, b, order, 0, eps, &e));
|
||||
res[1] = mp_obj_new_float(e);
|
||||
return mp_obj_new_tuple(2, res);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(integrate_quad_obj, 2, integrate_quad);
|
||||
#endif /* ULAB_INTEGRATE_HAS_QUAD */
|
||||
|
||||
static const mp_rom_map_elem_t ulab_scipy_integrate_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_integrate) },
|
||||
#if ULAB_INTEGRATE_HAS_TANHSINH
|
||||
{ MP_ROM_QSTR(MP_QSTR_tanhsinh), MP_ROM_PTR(&integrate_tanhsinh_obj) },
|
||||
#endif
|
||||
#if ULAB_INTEGRATE_HAS_ROMBERG
|
||||
{ MP_ROM_QSTR(MP_QSTR_romberg), MP_ROM_PTR(&integrate_romberg_obj) },
|
||||
#endif
|
||||
#if ULAB_INTEGRATE_HAS_SIMPSON
|
||||
{ MP_ROM_QSTR(MP_QSTR_simpson), MP_ROM_PTR(&integrate_simpson_obj) },
|
||||
#endif
|
||||
#if ULAB_INTEGRATE_HAS_QUAD
|
||||
{ MP_ROM_QSTR(MP_QSTR_quad), MP_ROM_PTR(&integrate_quad_obj) },
|
||||
#endif
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_integrate_globals, ulab_scipy_integrate_globals_table);
|
||||
|
||||
const mp_obj_module_t ulab_scipy_integrate_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_integrate_globals,
|
||||
};
|
||||
#if CIRCUITPY_ULAB
|
||||
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_integrate, ulab_scipy_integrate_module);
|
||||
#endif
|
||||
|
||||
34
code/scipy/integrate/integrate.h
Normal file
34
code/scipy/integrate/integrate.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2024 Harald Milz <hm@seneca.muc.de>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SCIPY_INTEGRATE_
|
||||
#define _SCIPY_INTEGRATE_
|
||||
|
||||
#include "../../ulab_tools.h"
|
||||
|
||||
extern const mp_obj_module_t ulab_scipy_integrate_module;
|
||||
|
||||
#if ULAB_INTEGRATE_HAS_TANHSINH
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(optimize_tanhsinh_obj);
|
||||
#endif
|
||||
#if ULAB_INTEGRATE_HAS_ROMBERG
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(optimize_romberg_obj);
|
||||
#endif
|
||||
#if ULAB_INTEGRATE_HAS_SIMPSON
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(optimize_simpson_obj);
|
||||
#endif
|
||||
#if ULAB_INTEGRATE_HAS_QUAD
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(optimize_quad_obj);
|
||||
#endif
|
||||
|
||||
#endif /* _SCIPY_INTEGRATE_ */
|
||||
|
||||
281
code/scipy/linalg/linalg.c
Normal file
281
code/scipy/linalg/linalg.c
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Vikas Udupa
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
|
||||
#include "../../ulab.h"
|
||||
#include "../../ulab_tools.h"
|
||||
#include "../../numpy/linalg/linalg_tools.h"
|
||||
#include "linalg.h"
|
||||
|
||||
#if ULAB_SCIPY_HAS_LINALG_MODULE
|
||||
//|
|
||||
//| import ulab.scipy
|
||||
//| import ulab.numpy
|
||||
//|
|
||||
//| """Linear algebra functions"""
|
||||
//|
|
||||
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
|
||||
//| def solve_triangular(A: ulab.numpy.ndarray, b: ulab.numpy.ndarray, lower: bool) -> ulab.numpy.ndarray:
|
||||
//| """
|
||||
//| :param ~ulab.numpy.ndarray A: a matrix
|
||||
//| :param ~ulab.numpy.ndarray b: a vector
|
||||
//| :param ~bool lower: if true, use only data contained in lower triangle of A, else use upper triangle of A
|
||||
//| :return: solution to the system A x = b. Shape of return matches b
|
||||
//| :raises TypeError: if A and b are not of type ndarray and are not dense
|
||||
//| :raises ValueError: if A is a singular matrix
|
||||
//|
|
||||
//| Solve the equation A x = b for x, assuming A is a triangular matrix"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t solve_triangular(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
|
||||
size_t i, j;
|
||||
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE} } ,
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE} } ,
|
||||
{ MP_QSTR_lower, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_TRUE } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type) || !mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("first two arguments must be ndarrays"));
|
||||
}
|
||||
|
||||
ndarray_obj_t *A = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
ndarray_obj_t *b = MP_OBJ_TO_PTR(args[1].u_obj);
|
||||
|
||||
if(!ndarray_is_dense(A) || !ndarray_is_dense(b)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("input must be a dense ndarray"));
|
||||
}
|
||||
|
||||
size_t A_rows = A->shape[ULAB_MAX_DIMS - 2];
|
||||
size_t A_cols = A->shape[ULAB_MAX_DIMS - 1];
|
||||
|
||||
uint8_t *A_arr = (uint8_t *)A->array;
|
||||
uint8_t *b_arr = (uint8_t *)b->array;
|
||||
|
||||
mp_float_t (*get_A_ele)(void *) = ndarray_get_float_function(A->dtype);
|
||||
mp_float_t (*get_b_ele)(void *) = ndarray_get_float_function(b->dtype);
|
||||
|
||||
uint8_t *temp_A = A_arr;
|
||||
|
||||
// check if input matrix A is singular
|
||||
for (i = 0; i < A_rows; i++) {
|
||||
if (MICROPY_FLOAT_C_FUN(fabs)(get_A_ele(A_arr)) < LINALG_EPSILON)
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("input matrix is singular"));
|
||||
A_arr += A->strides[ULAB_MAX_DIMS - 2];
|
||||
A_arr += A->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
|
||||
A_arr = temp_A;
|
||||
|
||||
ndarray_obj_t *x = ndarray_new_dense_ndarray(b->ndim, b->shape, NDARRAY_FLOAT);
|
||||
mp_float_t *x_arr = (mp_float_t *)x->array;
|
||||
|
||||
if (mp_obj_is_true(args[2].u_obj)) {
|
||||
// Solve the lower triangular matrix by iterating each row of A.
|
||||
// Start by finding the first unknown using the first row.
|
||||
// On finding this unknown, find the second unknown using the second row.
|
||||
// Continue the same till the last unknown is found using the last row.
|
||||
|
||||
for (i = 0; i < A_rows; i++) {
|
||||
mp_float_t sum = 0.0;
|
||||
for (j = 0; j < i; j++) {
|
||||
sum += (get_A_ele(A_arr) * (*x_arr++));
|
||||
A_arr += A->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
|
||||
sum = (get_b_ele(b_arr) - sum) / (get_A_ele(A_arr));
|
||||
*x_arr = sum;
|
||||
|
||||
x_arr -= j;
|
||||
A_arr -= A->strides[ULAB_MAX_DIMS - 1] * j;
|
||||
A_arr += A->strides[ULAB_MAX_DIMS - 2];
|
||||
b_arr += b->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
} else {
|
||||
// Solve the upper triangular matrix by iterating each row of A.
|
||||
// Start by finding the last unknown using the last row.
|
||||
// On finding this unknown, find the last-but-one unknown using the last-but-one row.
|
||||
// Continue the same till the first unknown is found using the first row.
|
||||
|
||||
A_arr += (A->strides[ULAB_MAX_DIMS - 2] * A_rows);
|
||||
b_arr += (b->strides[ULAB_MAX_DIMS - 1] * A_cols);
|
||||
x_arr += A_cols;
|
||||
|
||||
for (i = A_rows - 1; i < A_rows; i--) {
|
||||
mp_float_t sum = 0.0;
|
||||
for (j = i + 1; j < A_cols; j++) {
|
||||
sum += (get_A_ele(A_arr) * (*x_arr++));
|
||||
A_arr += A->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
|
||||
x_arr -= (j - i);
|
||||
A_arr -= (A->strides[ULAB_MAX_DIMS - 1] * (j - i));
|
||||
b_arr -= b->strides[ULAB_MAX_DIMS - 1];
|
||||
|
||||
sum = (get_b_ele(b_arr) - sum) / get_A_ele(A_arr);
|
||||
*x_arr = sum;
|
||||
|
||||
A_arr -= A->strides[ULAB_MAX_DIMS - 2];
|
||||
}
|
||||
}
|
||||
|
||||
return MP_OBJ_FROM_PTR(x);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(linalg_solve_triangular_obj, 2, solve_triangular);
|
||||
|
||||
//| def cho_solve(L: ulab.numpy.ndarray, b: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
|
||||
//| """
|
||||
//| :param ~ulab.numpy.ndarray L: the lower triangular, Cholesky factorization of A
|
||||
//| :param ~ulab.numpy.ndarray b: right-hand-side vector b
|
||||
//| :return: solution to the system A x = b. Shape of return matches b
|
||||
//| :raises TypeError: if L and b are not of type ndarray and are not dense
|
||||
//|
|
||||
//| Solve the linear equations A x = b, given the Cholesky factorization of A as input"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t cho_solve(mp_obj_t _L, mp_obj_t _b) {
|
||||
|
||||
if(!mp_obj_is_type(_L, &ulab_ndarray_type) || !mp_obj_is_type(_b, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("first two arguments must be ndarrays"));
|
||||
}
|
||||
|
||||
ndarray_obj_t *L = MP_OBJ_TO_PTR(_L);
|
||||
ndarray_obj_t *b = MP_OBJ_TO_PTR(_b);
|
||||
|
||||
if(!ndarray_is_dense(L) || !ndarray_is_dense(b)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("input must be a dense ndarray"));
|
||||
}
|
||||
|
||||
mp_float_t (*get_L_ele)(void *) = ndarray_get_float_function(L->dtype);
|
||||
mp_float_t (*get_b_ele)(void *) = ndarray_get_float_function(b->dtype);
|
||||
void (*set_L_ele)(void *, mp_float_t) = ndarray_set_float_function(L->dtype);
|
||||
|
||||
size_t L_rows = L->shape[ULAB_MAX_DIMS - 2];
|
||||
size_t L_cols = L->shape[ULAB_MAX_DIMS - 1];
|
||||
|
||||
// Obtain transpose of the input matrix L in L_t
|
||||
size_t L_t_shape[ULAB_MAX_DIMS];
|
||||
size_t L_t_rows = L_t_shape[ULAB_MAX_DIMS - 2] = L_cols;
|
||||
size_t L_t_cols = L_t_shape[ULAB_MAX_DIMS - 1] = L_rows;
|
||||
ndarray_obj_t *L_t = ndarray_new_dense_ndarray(L->ndim, L_t_shape, L->dtype);
|
||||
|
||||
uint8_t *L_arr = (uint8_t *)L->array;
|
||||
uint8_t *L_t_arr = (uint8_t *)L_t->array;
|
||||
uint8_t *b_arr = (uint8_t *)b->array;
|
||||
|
||||
size_t i, j;
|
||||
|
||||
uint8_t *L_ptr = L_arr;
|
||||
uint8_t *L_t_ptr = L_t_arr;
|
||||
for (i = 0; i < L_rows; i++) {
|
||||
for (j = 0; j < L_cols; j++) {
|
||||
set_L_ele(L_t_ptr, get_L_ele(L_ptr));
|
||||
L_t_ptr += L_t->strides[ULAB_MAX_DIMS - 2];
|
||||
L_ptr += L->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
|
||||
L_t_ptr -= j * L_t->strides[ULAB_MAX_DIMS - 2];
|
||||
L_t_ptr += L_t->strides[ULAB_MAX_DIMS - 1];
|
||||
L_ptr -= j * L->strides[ULAB_MAX_DIMS - 1];
|
||||
L_ptr += L->strides[ULAB_MAX_DIMS - 2];
|
||||
}
|
||||
|
||||
ndarray_obj_t *x = ndarray_new_dense_ndarray(b->ndim, b->shape, NDARRAY_FLOAT);
|
||||
mp_float_t *x_arr = (mp_float_t *)x->array;
|
||||
|
||||
ndarray_obj_t *y = ndarray_new_dense_ndarray(b->ndim, b->shape, NDARRAY_FLOAT);
|
||||
mp_float_t *y_arr = (mp_float_t *)y->array;
|
||||
|
||||
// solve L y = b to obtain y, where L_t x = y
|
||||
for (i = 0; i < L_rows; i++) {
|
||||
mp_float_t sum = 0.0;
|
||||
for (j = 0; j < i; j++) {
|
||||
sum += (get_L_ele(L_arr) * (*y_arr++));
|
||||
L_arr += L->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
|
||||
sum = (get_b_ele(b_arr) - sum) / (get_L_ele(L_arr));
|
||||
*y_arr = sum;
|
||||
|
||||
y_arr -= j;
|
||||
L_arr -= L->strides[ULAB_MAX_DIMS - 1] * j;
|
||||
L_arr += L->strides[ULAB_MAX_DIMS - 2];
|
||||
b_arr += b->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
|
||||
// using y, solve L_t x = y to obtain x
|
||||
L_t_arr += (L_t->strides[ULAB_MAX_DIMS - 2] * L_t_rows);
|
||||
y_arr += L_t_cols;
|
||||
x_arr += L_t_cols;
|
||||
|
||||
for (i = L_t_rows - 1; i < L_t_rows; i--) {
|
||||
mp_float_t sum = 0.0;
|
||||
for (j = i + 1; j < L_t_cols; j++) {
|
||||
sum += (get_L_ele(L_t_arr) * (*x_arr++));
|
||||
L_t_arr += L_t->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
|
||||
x_arr -= (j - i);
|
||||
L_t_arr -= (L_t->strides[ULAB_MAX_DIMS - 1] * (j - i));
|
||||
y_arr--;
|
||||
|
||||
sum = ((*y_arr) - sum) / get_L_ele(L_t_arr);
|
||||
*x_arr = sum;
|
||||
|
||||
L_t_arr -= L_t->strides[ULAB_MAX_DIMS - 2];
|
||||
}
|
||||
|
||||
return MP_OBJ_FROM_PTR(x);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(linalg_cho_solve_obj, cho_solve);
|
||||
|
||||
#endif
|
||||
|
||||
static const mp_rom_map_elem_t ulab_scipy_linalg_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_linalg) },
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
#if ULAB_SCIPY_LINALG_HAS_SOLVE_TRIANGULAR
|
||||
{ MP_ROM_QSTR(MP_QSTR_solve_triangular), MP_ROM_PTR(&linalg_solve_triangular_obj) },
|
||||
#endif
|
||||
#if ULAB_SCIPY_LINALG_HAS_CHO_SOLVE
|
||||
{ MP_ROM_QSTR(MP_QSTR_cho_solve), MP_ROM_PTR(&linalg_cho_solve_obj) },
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_linalg_globals, ulab_scipy_linalg_globals_table);
|
||||
|
||||
const mp_obj_module_t ulab_scipy_linalg_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_linalg_globals,
|
||||
};
|
||||
#if CIRCUITPY_ULAB
|
||||
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_linalg, ulab_scipy_linalg_module);
|
||||
#endif
|
||||
#endif
|
||||
21
code/scipy/linalg/linalg.h
Normal file
21
code/scipy/linalg/linalg.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Vikas Udupa
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SCIPY_LINALG_
|
||||
#define _SCIPY_LINALG_
|
||||
|
||||
extern const mp_obj_module_t ulab_scipy_linalg_module;
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(linalg_solve_triangular_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(linalg_cho_solve_obj);
|
||||
|
||||
#endif /* _SCIPY_LINALG_ */
|
||||
417
code/scipy/optimize/optimize.c
Normal file
417
code/scipy/optimize/optimize.c
Normal file
|
|
@ -0,0 +1,417 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
|
||||
* 2020 Scott Shawcroft for Adafruit Industries
|
||||
* 2020-2021 Zoltán Vörös
|
||||
* 2020 Taku Fukada
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
|
||||
#include "../../ndarray.h"
|
||||
#include "../../ulab.h"
|
||||
#include "../../ulab_tools.h"
|
||||
#include "optimize.h"
|
||||
|
||||
ULAB_DEFINE_FLOAT_CONST(xtolerance, MICROPY_FLOAT_CONST(2.4e-7), 0x3480d959UL, 0x3e901b2b29a4692bULL);
|
||||
ULAB_DEFINE_FLOAT_CONST(rtolerance, MICROPY_FLOAT_CONST(0.0), 0UL, 0ULL);
|
||||
|
||||
static mp_float_t optimize_python_call(const mp_obj_type_t *type, mp_obj_t fun, mp_float_t x, mp_obj_t *fargs, uint8_t nparams) {
|
||||
// Helper function for calculating the value of f(x, a, b, c, ...),
|
||||
// where f is defined in python. Takes a float, returns a float.
|
||||
// The array of mp_obj_t type must be supplied, as must the number of parameters (a, b, c...) in nparams
|
||||
fargs[0] = mp_obj_new_float(x);
|
||||
return mp_obj_get_float(MP_OBJ_TYPE_GET_SLOT(type, call)(fun, nparams+1, 0, fargs));
|
||||
}
|
||||
|
||||
#if ULAB_SCIPY_OPTIMIZE_HAS_BISECT
|
||||
//| def bisect(
|
||||
//| fun: Callable[[float], float],
|
||||
//| a: float,
|
||||
//| b: float,
|
||||
//| *,
|
||||
//| xtol: float = 2.4e-7,
|
||||
//| maxiter: int = 100
|
||||
//| ) -> float:
|
||||
//| """
|
||||
//| :param callable f: The function to bisect
|
||||
//| :param float a: The left side of the interval
|
||||
//| :param float b: The right side of the interval
|
||||
//| :param float xtol: The tolerance value
|
||||
//| :param float maxiter: The maximum number of iterations to perform
|
||||
//|
|
||||
//| Find a solution (zero) of the function ``f(x)`` on the interval
|
||||
//| (``a``..``b``) using the bisection method. The result is accurate to within
|
||||
//| ``xtol`` unless more than ``maxiter`` steps are required."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t optimize_bisect(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
// Simple bisection routine
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_xtol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(xtolerance)} },
|
||||
{ MP_QSTR_maxiter, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 100} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
mp_obj_t fun = args[0].u_obj;
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a function"));
|
||||
}
|
||||
mp_float_t xtol = mp_obj_get_float(args[3].u_obj);
|
||||
mp_obj_t fargs[1];
|
||||
mp_float_t left, right;
|
||||
mp_float_t x_mid;
|
||||
mp_float_t a = mp_obj_get_float(args[1].u_obj);
|
||||
mp_float_t b = mp_obj_get_float(args[2].u_obj);
|
||||
left = optimize_python_call(type, fun, a, fargs, 0);
|
||||
right = optimize_python_call(type, fun, b, fargs, 0);
|
||||
if(left * right > 0) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("function has the same sign at the ends of interval"));
|
||||
}
|
||||
mp_float_t rtb = left < MICROPY_FLOAT_CONST(0.0) ? a : b;
|
||||
mp_float_t dx = left < MICROPY_FLOAT_CONST(0.0) ? b - a : a - b;
|
||||
if(args[4].u_int < 0) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("maxiter should be > 0"));
|
||||
}
|
||||
for(uint16_t i=0; i < args[4].u_int; i++) {
|
||||
dx *= MICROPY_FLOAT_CONST(0.5);
|
||||
x_mid = rtb + dx;
|
||||
if(optimize_python_call(type, fun, x_mid, fargs, 0) < MICROPY_FLOAT_CONST(0.0)) {
|
||||
rtb = x_mid;
|
||||
}
|
||||
if(MICROPY_FLOAT_C_FUN(fabs)(dx) < xtol) break;
|
||||
}
|
||||
return mp_obj_new_float(rtb);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(optimize_bisect_obj, 3, optimize_bisect);
|
||||
#endif
|
||||
|
||||
#if ULAB_SCIPY_OPTIMIZE_HAS_FMIN
|
||||
//| def fmin(
|
||||
//| fun: Callable[[float], float],
|
||||
//| x0: float,
|
||||
//| *,
|
||||
//| xatol: float = 2.4e-7,
|
||||
//| fatol: float = 2.4e-7,
|
||||
//| maxiter: int = 200
|
||||
//| ) -> float:
|
||||
//| """
|
||||
//| :param callable f: The function to bisect
|
||||
//| :param float x0: The initial x value
|
||||
//| :param float xatol: The absolute tolerance value
|
||||
//| :param float fatol: The relative tolerance value
|
||||
//|
|
||||
//| Find a minimum of the function ``f(x)`` using the downhill simplex method.
|
||||
//| The located ``x`` is within ``fxtol`` of the actual minimum, and ``f(x)``
|
||||
//| is within ``fatol`` of the actual minimum unless more than ``maxiter``
|
||||
//| steps are requried."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t optimize_fmin(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
// downhill simplex method in 1D
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_xatol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(xtolerance)} },
|
||||
{ MP_QSTR_fatol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(xtolerance)} },
|
||||
{ MP_QSTR_maxiter, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 200} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
mp_obj_t fun = args[0].u_obj;
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a function"));
|
||||
}
|
||||
|
||||
// parameters controlling convergence conditions
|
||||
mp_float_t xatol = mp_obj_get_float(args[2].u_obj);
|
||||
mp_float_t fatol = mp_obj_get_float(args[3].u_obj);
|
||||
if(args[4].u_int <= 0) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("maxiter must be > 0"));
|
||||
}
|
||||
uint16_t maxiter = (uint16_t)args[4].u_int;
|
||||
|
||||
mp_float_t x0 = mp_obj_get_float(args[1].u_obj);
|
||||
mp_float_t x1 = MICROPY_FLOAT_C_FUN(fabs)(x0) > OPTIMIZE_EPSILON ? (MICROPY_FLOAT_CONST(1.0) + OPTIMIZE_NONZDELTA) * x0 : OPTIMIZE_ZDELTA;
|
||||
mp_obj_t fargs[1];
|
||||
mp_float_t f0 = optimize_python_call(type, fun, x0, fargs, 0);
|
||||
mp_float_t f1 = optimize_python_call(type, fun, x1, fargs, 0);
|
||||
if(f1 < f0) {
|
||||
SWAP(mp_float_t, x0, x1);
|
||||
SWAP(mp_float_t, f0, f1);
|
||||
}
|
||||
for(uint16_t i=0; i < maxiter; i++) {
|
||||
uint8_t shrink = 0;
|
||||
f0 = optimize_python_call(type, fun, x0, fargs, 0);
|
||||
f1 = optimize_python_call(type, fun, x1, fargs, 0);
|
||||
|
||||
// reflection
|
||||
mp_float_t xr = (MICROPY_FLOAT_CONST(1.0) + OPTIMIZE_ALPHA) * x0 - OPTIMIZE_ALPHA * x1;
|
||||
mp_float_t fr = optimize_python_call(type, fun, xr, fargs, 0);
|
||||
if(fr < f0) { // expansion
|
||||
mp_float_t xe = (1 + OPTIMIZE_ALPHA * OPTIMIZE_BETA) * x0 - OPTIMIZE_ALPHA * OPTIMIZE_BETA * x1;
|
||||
mp_float_t fe = optimize_python_call(type, fun, xe, fargs, 0);
|
||||
if(fe < fr) {
|
||||
x1 = xe;
|
||||
f1 = fe;
|
||||
} else {
|
||||
x1 = xr;
|
||||
f1 = fr;
|
||||
}
|
||||
} else {
|
||||
if(fr < f1) { // contraction
|
||||
mp_float_t xc = (1 + OPTIMIZE_GAMMA * OPTIMIZE_ALPHA) * x0 - OPTIMIZE_GAMMA * OPTIMIZE_ALPHA * x1;
|
||||
mp_float_t fc = optimize_python_call(type, fun, xc, fargs, 0);
|
||||
if(fc < fr) {
|
||||
x1 = xc;
|
||||
f1 = fc;
|
||||
} else {
|
||||
shrink = 1;
|
||||
}
|
||||
} else { // inside contraction
|
||||
mp_float_t xc = (MICROPY_FLOAT_CONST(1.0) - OPTIMIZE_GAMMA) * x0 + OPTIMIZE_GAMMA * x1;
|
||||
mp_float_t fc = optimize_python_call(type, fun, xc, fargs, 0);
|
||||
if(fc < f1) {
|
||||
x1 = xc;
|
||||
f1 = fc;
|
||||
} else {
|
||||
shrink = 1;
|
||||
}
|
||||
}
|
||||
if(shrink == 1) {
|
||||
x1 = x0 + OPTIMIZE_DELTA * (x1 - x0);
|
||||
f1 = optimize_python_call(type, fun, x1, fargs, 0);
|
||||
}
|
||||
if((MICROPY_FLOAT_C_FUN(fabs)(f1 - f0) < fatol) ||
|
||||
(MICROPY_FLOAT_C_FUN(fabs)(x1 - x0) < xatol)) {
|
||||
break;
|
||||
}
|
||||
if(f1 < f0) {
|
||||
SWAP(mp_float_t, x0, x1);
|
||||
SWAP(mp_float_t, f0, f1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return mp_obj_new_float(x0);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(optimize_fmin_obj, 2, optimize_fmin);
|
||||
#endif
|
||||
|
||||
#if ULAB_SCIPY_OPTIMIZE_HAS_CURVE_FIT
|
||||
static void optimize_jacobi(const mp_obj_type_t *type, mp_obj_t fun, mp_float_t *x, mp_float_t *y, uint16_t len, mp_float_t *params, uint8_t nparams, mp_float_t *jacobi, mp_float_t *grad) {
|
||||
/* Calculates the Jacobian and the gradient of the cost function
|
||||
*
|
||||
* The entries in the Jacobian are
|
||||
* J(m, n) = de_m/da_n,
|
||||
*
|
||||
* where
|
||||
*
|
||||
* e_m = (f(x_m, a1, a2, ...) - y_m)/sigma_m is the error at x_m,
|
||||
*
|
||||
* and
|
||||
*
|
||||
* a1, a2, ..., a_n are the free parameters
|
||||
*/
|
||||
mp_obj_t *fargs0 = m_new(mp_obj_t, lenp+1);
|
||||
mp_obj_t *fargs1 = m_new(mp_obj_t, lenp+1);
|
||||
for(uint8_t p=0; p < nparams; p++) {
|
||||
fargs0[p+1] = mp_obj_new_float(params[p]);
|
||||
fargs1[p+1] = mp_obj_new_float(params[p]);
|
||||
}
|
||||
for(uint8_t p=0; p < nparams; p++) {
|
||||
mp_float_t da = params[p] != MICROPY_FLOAT_CONST(0.0) ? (MICROPY_FLOAT_CONST(1.0) + APPROX_NONZDELTA) * params[p] : APPROX_ZDELTA;
|
||||
fargs1[p+1] = mp_obj_new_float(params[p] + da);
|
||||
grad[p] = MICROPY_FLOAT_CONST(0.0);
|
||||
for(uint16_t i=0; i < len; i++) {
|
||||
mp_float_t f0 = optimize_python_call(type, fun, x[i], fargs0, nparams);
|
||||
mp_float_t f1 = optimize_python_call(type, fun, x[i], fargs1, nparams);
|
||||
jacobi[i*nparamp+p] = (f1 - f0) / da;
|
||||
grad[p] += (f0 - y[i]) * jacobi[i*nparamp+p];
|
||||
}
|
||||
fargs1[p+1] = fargs0[p+1]; // set back to the original value
|
||||
}
|
||||
}
|
||||
|
||||
static void optimize_delta(mp_float_t *jacobi, mp_float_t *grad, uint16_t len, uint8_t nparams, mp_float_t lambda) {
|
||||
//
|
||||
}
|
||||
|
||||
mp_obj_t optimize_curve_fit(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
// Levenberg-Marquardt non-linear fit
|
||||
// The implementation follows the introductory discussion in Mark Tanstrum's paper, https://arxiv.org/abs/1201.5885
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_p0, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_xatol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&xtolerance)} },
|
||||
{ MP_QSTR_fatol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&xtolerance)} },
|
||||
{ MP_QSTR_maxiter, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
mp_obj_t fun = args[0].u_obj;
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a function"));
|
||||
}
|
||||
|
||||
mp_obj_t x_obj = args[1].u_obj;
|
||||
mp_obj_t y_obj = args[2].u_obj;
|
||||
mp_obj_t p0_obj = args[3].u_obj;
|
||||
if(!ndarray_object_is_array_like(x_obj) || !ndarray_object_is_array_like(y_obj)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("data must be iterable"));
|
||||
}
|
||||
if(!ndarray_object_is_nditerable(p0_obj)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("initial values must be iterable"));
|
||||
}
|
||||
size_t len = (size_t)mp_obj_get_int(mp_obj_len_maybe(x_obj));
|
||||
uint8_t lenp = (uint8_t)mp_obj_get_int(mp_obj_len_maybe(p0_obj));
|
||||
if(len != (uint16_t)mp_obj_get_int(mp_obj_len_maybe(y_obj))) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("data must be of equal length"));
|
||||
}
|
||||
|
||||
mp_float_t *x = m_new(mp_float_t, len);
|
||||
fill_array_iterable(x, x_obj);
|
||||
mp_float_t *y = m_new(mp_float_t, len);
|
||||
fill_array_iterable(y, y_obj);
|
||||
mp_float_t *p0 = m_new(mp_float_t, lenp);
|
||||
fill_array_iterable(p0, p0_obj);
|
||||
mp_float_t *grad = m_new(mp_float_t, len);
|
||||
mp_float_t *jacobi = m_new(mp_float_t, len*len);
|
||||
mp_obj_t *fargs = m_new(mp_obj_t, lenp+1);
|
||||
|
||||
m_del(mp_float_t, p0, lenp);
|
||||
// parameters controlling convergence conditions
|
||||
//mp_float_t xatol = mp_obj_get_float(args[2].u_obj);
|
||||
//mp_float_t fatol = mp_obj_get_float(args[3].u_obj);
|
||||
|
||||
// this has finite binary representation; we will multiply/divide by 4
|
||||
//mp_float_t lambda = 0.0078125;
|
||||
|
||||
//linalg_invert_matrix(mp_float_t *data, size_t N)
|
||||
|
||||
m_del(mp_float_t, x, len);
|
||||
m_del(mp_float_t, y, len);
|
||||
m_del(mp_float_t, grad, len);
|
||||
m_del(mp_float_t, jacobi, len*len);
|
||||
m_del(mp_obj_t, fargs, lenp+1);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(optimize_curve_fit_obj, 2, optimize_curve_fit);
|
||||
#endif
|
||||
|
||||
#if ULAB_SCIPY_OPTIMIZE_HAS_NEWTON
|
||||
//| def newton(
|
||||
//| fun: Callable[[float], float],
|
||||
//| x0: float,
|
||||
//| *,
|
||||
//| xtol: float = 2.4e-7,
|
||||
//| rtol: float = 0.0,
|
||||
//| maxiter: int = 50
|
||||
//| ) -> float:
|
||||
//| """
|
||||
//| :param callable f: The function to bisect
|
||||
//| :param float x0: The initial x value
|
||||
//| :param float xtol: The absolute tolerance value
|
||||
//| :param float rtol: The relative tolerance value
|
||||
//| :param float maxiter: The maximum number of iterations to perform
|
||||
//|
|
||||
//| Find a solution (zero) of the function ``f(x)`` using Newton's Method.
|
||||
//| The result is accurate to within ``xtol * rtol * |f(x)|`` unless more than
|
||||
//| ``maxiter`` steps are requried."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
static mp_obj_t optimize_newton(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
// this is actually the secant method, as the first derivative of the function
|
||||
// is not accepted as an argument. The function whose root we want to solve for
|
||||
// must depend on a single variable without parameters, i.e., f(x)
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(xtolerance) } },
|
||||
{ MP_QSTR_rtol, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(rtolerance) } },
|
||||
{ MP_QSTR_maxiter, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = 50 } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
mp_obj_t fun = args[0].u_obj;
|
||||
const mp_obj_type_t *type = mp_obj_get_type(fun);
|
||||
if(!MP_OBJ_TYPE_HAS_SLOT(type, call)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("first argument must be a function"));
|
||||
}
|
||||
mp_float_t x = mp_obj_get_float(args[1].u_obj);
|
||||
mp_float_t tol = mp_obj_get_float(args[2].u_obj);
|
||||
mp_float_t rtol = mp_obj_get_float(args[3].u_obj);
|
||||
mp_float_t dx, df, fx;
|
||||
dx = x > MICROPY_FLOAT_CONST(0.0) ? OPTIMIZE_EPS * x : -OPTIMIZE_EPS * x;
|
||||
mp_obj_t fargs[1];
|
||||
if(args[4].u_int <= 0) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("maxiter must be > 0"));
|
||||
}
|
||||
for(uint16_t i=0; i < args[4].u_int; i++) {
|
||||
fx = optimize_python_call(type, fun, x, fargs, 0);
|
||||
df = (optimize_python_call(type, fun, x + dx, fargs, 0) - fx) / dx;
|
||||
dx = fx / df;
|
||||
x -= dx;
|
||||
if(MICROPY_FLOAT_C_FUN(fabs)(dx) < (tol + rtol * MICROPY_FLOAT_C_FUN(fabs)(x))) break;
|
||||
}
|
||||
return mp_obj_new_float(x);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(optimize_newton_obj, 2, optimize_newton);
|
||||
#endif
|
||||
|
||||
static const mp_rom_map_elem_t ulab_scipy_optimize_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_optimize) },
|
||||
#if ULAB_SCIPY_OPTIMIZE_HAS_BISECT
|
||||
{ MP_ROM_QSTR(MP_QSTR_bisect), MP_ROM_PTR(&optimize_bisect_obj) },
|
||||
#endif
|
||||
#if ULAB_SCIPY_OPTIMIZE_HAS_CURVE_FIT
|
||||
{ MP_ROM_QSTR(MP_QSTR_curve_fit), MP_ROM_PTR(&optimize_curve_fit_obj) },
|
||||
#endif
|
||||
#if ULAB_SCIPY_OPTIMIZE_HAS_FMIN
|
||||
{ MP_ROM_QSTR(MP_QSTR_fmin), MP_ROM_PTR(&optimize_fmin_obj) },
|
||||
#endif
|
||||
#if ULAB_SCIPY_OPTIMIZE_HAS_NEWTON
|
||||
{ MP_ROM_QSTR(MP_QSTR_newton), MP_ROM_PTR(&optimize_newton_obj) },
|
||||
#endif
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_optimize_globals, ulab_scipy_optimize_globals_table);
|
||||
|
||||
const mp_obj_module_t ulab_scipy_optimize_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_optimize_globals,
|
||||
};
|
||||
#if CIRCUITPY_ULAB
|
||||
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_optimize, ulab_scipy_optimize_module);
|
||||
#endif
|
||||
41
code/scipy/optimize/optimize.h
Normal file
41
code/scipy/optimize/optimize.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2021 Zoltán Vörös
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SCIPY_OPTIMIZE_
|
||||
#define _SCIPY_OPTIMIZE_
|
||||
|
||||
#include "../../ulab_tools.h"
|
||||
|
||||
#ifndef OPTIMIZE_EPSILON
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
#define OPTIMIZE_EPSILON MICROPY_FLOAT_CONST(1.2e-7)
|
||||
#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
|
||||
#define OPTIMIZE_EPSILON MICROPY_FLOAT_CONST(2.3e-16)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define OPTIMIZE_EPS MICROPY_FLOAT_CONST(1.0e-4)
|
||||
#define OPTIMIZE_NONZDELTA MICROPY_FLOAT_CONST(0.05)
|
||||
#define OPTIMIZE_ZDELTA MICROPY_FLOAT_CONST(0.00025)
|
||||
#define OPTIMIZE_ALPHA MICROPY_FLOAT_CONST(1.0)
|
||||
#define OPTIMIZE_BETA MICROPY_FLOAT_CONST(2.0)
|
||||
#define OPTIMIZE_GAMMA MICROPY_FLOAT_CONST(0.5)
|
||||
#define OPTIMIZE_DELTA MICROPY_FLOAT_CONST(0.5)
|
||||
|
||||
extern const mp_obj_module_t ulab_scipy_optimize_module;
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(optimize_bisect_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(optimize_curve_fit_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(optimize_fmin_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(optimize_newton_obj);
|
||||
|
||||
#endif /* _SCIPY_OPTIMIZE_ */
|
||||
58
code/scipy/scipy.c
Normal file
58
code/scipy/scipy.c
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
|
||||
* 2020 Scott Shawcroft for Adafruit Industries
|
||||
* 2020-2021 Zoltán Vörös
|
||||
* 2020 Taku Fukada
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include "py/runtime.h"
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "optimize/optimize.h"
|
||||
#include "signal/signal.h"
|
||||
#include "special/special.h"
|
||||
#include "linalg/linalg.h"
|
||||
#include "integrate/integrate.h"
|
||||
|
||||
|
||||
#if ULAB_HAS_SCIPY
|
||||
|
||||
//| """Compatibility layer for scipy"""
|
||||
//|
|
||||
|
||||
static const mp_rom_map_elem_t ulab_scipy_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_scipy) },
|
||||
#if ULAB_SCIPY_HAS_INTEGRATE_MODULE
|
||||
{ MP_ROM_QSTR(MP_QSTR_integrate), MP_ROM_PTR(&ulab_scipy_integrate_module) },
|
||||
#endif
|
||||
#if ULAB_SCIPY_HAS_LINALG_MODULE
|
||||
{ MP_ROM_QSTR(MP_QSTR_linalg), MP_ROM_PTR(&ulab_scipy_linalg_module) },
|
||||
#endif
|
||||
#if ULAB_SCIPY_HAS_OPTIMIZE_MODULE
|
||||
{ MP_ROM_QSTR(MP_QSTR_optimize), MP_ROM_PTR(&ulab_scipy_optimize_module) },
|
||||
#endif
|
||||
#if ULAB_SCIPY_HAS_SIGNAL_MODULE
|
||||
{ MP_ROM_QSTR(MP_QSTR_signal), MP_ROM_PTR(&ulab_scipy_signal_module) },
|
||||
#endif
|
||||
#if ULAB_SCIPY_HAS_SPECIAL_MODULE
|
||||
{ MP_ROM_QSTR(MP_QSTR_special), MP_ROM_PTR(&ulab_scipy_special_module) },
|
||||
#endif
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_globals, ulab_scipy_globals_table);
|
||||
|
||||
const mp_obj_module_t ulab_scipy_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_globals,
|
||||
};
|
||||
#if CIRCUITPY_ULAB
|
||||
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy, ulab_scipy_module);
|
||||
#endif
|
||||
#endif /* ULAB_HAS_SCIPY */
|
||||
21
code/scipy/scipy.h
Normal file
21
code/scipy/scipy.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2021 Zoltán Vörös
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SCIPY_
|
||||
#define _SCIPY_
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "../ndarray.h"
|
||||
|
||||
extern const mp_obj_module_t ulab_scipy_module;
|
||||
|
||||
#endif /* _SCIPY_ */
|
||||
138
code/scipy/signal/signal.c
Normal file
138
code/scipy/signal/signal.c
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
|
||||
* 2020 Scott Shawcroft for Adafruit Industries
|
||||
* 2020-2021 Zoltán Vörös
|
||||
* 2020 Taku Fukada
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include "py/runtime.h"
|
||||
|
||||
#include "../../ulab.h"
|
||||
#include "../../ndarray.h"
|
||||
#include "../../numpy/carray/carray_tools.h"
|
||||
|
||||
#if ULAB_SCIPY_SIGNAL_HAS_SOSFILT & ULAB_MAX_DIMS > 1
|
||||
static void signal_sosfilt_array(mp_float_t *x, const mp_float_t *coeffs, mp_float_t *zf, const size_t len) {
|
||||
for(size_t i=0; i < len; i++) {
|
||||
mp_float_t xn = *x;
|
||||
*x = coeffs[0] * xn + zf[0];
|
||||
zf[0] = zf[1] + coeffs[1] * xn - coeffs[4] * *x;
|
||||
zf[1] = coeffs[2] * xn - coeffs[5] * *x;
|
||||
x++;
|
||||
}
|
||||
x -= len;
|
||||
}
|
||||
|
||||
mp_obj_t signal_sosfilt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_sos, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_zi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
if(!ndarray_object_is_array_like(args[0].u_obj) || !ndarray_object_is_array_like(args[1].u_obj)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("sosfilt requires iterable arguments"));
|
||||
}
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
if(mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) {
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[1].u_obj);
|
||||
COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
|
||||
}
|
||||
#endif
|
||||
size_t lenx = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[1].u_obj));
|
||||
ndarray_obj_t *y = ndarray_new_linear_array(lenx, NDARRAY_FLOAT);
|
||||
mp_float_t *yarray = (mp_float_t *)y->array;
|
||||
mp_float_t coeffs[6];
|
||||
if(mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) {
|
||||
ndarray_obj_t *inarray = MP_OBJ_TO_PTR(args[1].u_obj);
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
if(inarray->ndim > 1) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("input must be one-dimensional"));
|
||||
}
|
||||
#endif
|
||||
uint8_t *iarray = (uint8_t *)inarray->array;
|
||||
for(size_t i=0; i < lenx; i++) {
|
||||
*yarray++ = ndarray_get_float_value(iarray, inarray->dtype);
|
||||
iarray += inarray->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
yarray -= lenx;
|
||||
} else {
|
||||
fill_array_iterable(yarray, args[1].u_obj);
|
||||
}
|
||||
|
||||
mp_obj_iter_buf_t iter_buf;
|
||||
mp_obj_t item, iterable = mp_getiter(args[0].u_obj, &iter_buf);
|
||||
size_t lensos = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[0].u_obj));
|
||||
|
||||
size_t *shape = ndarray_shape_vector(0, 0, lensos, 2);
|
||||
ndarray_obj_t *zf = ndarray_new_dense_ndarray(2, shape, NDARRAY_FLOAT);
|
||||
mp_float_t *zf_array = (mp_float_t *)zf->array;
|
||||
|
||||
if(args[2].u_obj != mp_const_none) {
|
||||
if(!mp_obj_is_type(args[2].u_obj, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("zi must be an ndarray"));
|
||||
} else {
|
||||
ndarray_obj_t *zi = MP_OBJ_TO_PTR(args[2].u_obj);
|
||||
if((zi->shape[ULAB_MAX_DIMS - 2] != lensos) || (zi->shape[ULAB_MAX_DIMS - 1] != 2)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("zi must be of shape (n_section, 2)"));
|
||||
}
|
||||
if(zi->dtype != NDARRAY_FLOAT) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("zi must be of float type"));
|
||||
}
|
||||
// TODO: this won't work with sparse arrays
|
||||
memcpy(zf_array, zi->array, 2*lensos*sizeof(mp_float_t));
|
||||
}
|
||||
}
|
||||
while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
if(mp_obj_get_int(mp_obj_len_maybe(item)) != 6) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("sos array must be of shape (n_section, 6)"));
|
||||
} else {
|
||||
fill_array_iterable(coeffs, item);
|
||||
if(coeffs[3] != MICROPY_FLOAT_CONST(1.0)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("sos[:, 3] should be all ones"));
|
||||
}
|
||||
signal_sosfilt_array(yarray, coeffs, zf_array, lenx);
|
||||
zf_array += 2;
|
||||
}
|
||||
}
|
||||
if(args[2].u_obj == mp_const_none) {
|
||||
return MP_OBJ_FROM_PTR(y);
|
||||
} else {
|
||||
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
|
||||
tuple->items[0] = MP_OBJ_FROM_PTR(y);
|
||||
tuple->items[1] = MP_OBJ_FROM_PTR(zf);
|
||||
return MP_OBJ_FROM_PTR(tuple);
|
||||
}
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(signal_sosfilt_obj, 2, signal_sosfilt);
|
||||
#endif /* ULAB_SCIPY_SIGNAL_HAS_SOSFILT */
|
||||
|
||||
static const mp_rom_map_elem_t ulab_scipy_signal_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_signal) },
|
||||
#if ULAB_SCIPY_SIGNAL_HAS_SOSFILT & ULAB_MAX_DIMS > 1
|
||||
{ MP_ROM_QSTR(MP_QSTR_sosfilt), MP_ROM_PTR(&signal_sosfilt_obj) },
|
||||
#endif
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_signal_globals, ulab_scipy_signal_globals_table);
|
||||
|
||||
const mp_obj_module_t ulab_scipy_signal_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_signal_globals,
|
||||
};
|
||||
#if CIRCUITPY_ULAB
|
||||
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_signal, ulab_scipy_signal_module);
|
||||
#endif
|
||||
23
code/scipy/signal/signal.h
Normal file
23
code/scipy/signal/signal.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2021 Zoltán Vörös
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SCIPY_SIGNAL_
|
||||
#define _SCIPY_SIGNAL_
|
||||
|
||||
#include "../../ulab.h"
|
||||
#include "../../ndarray.h"
|
||||
|
||||
extern const mp_obj_module_t ulab_scipy_signal_module;
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(signal_sosfilt_obj);
|
||||
|
||||
#endif /* _SCIPY_SIGNAL_ */
|
||||
45
code/scipy/special/special.c
Normal file
45
code/scipy/special/special.c
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
|
||||
* 2020 Scott Shawcroft for Adafruit Industries
|
||||
* 2020-2021 Zoltán Vörös
|
||||
* 2020 Taku Fukada
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include "py/runtime.h"
|
||||
|
||||
#include "../../ulab.h"
|
||||
#include "../../numpy/vector.h"
|
||||
|
||||
static const mp_rom_map_elem_t ulab_scipy_special_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_special) },
|
||||
#if ULAB_SCIPY_SPECIAL_HAS_ERF
|
||||
{ MP_ROM_QSTR(MP_QSTR_erf), MP_ROM_PTR(&vector_erf_obj) },
|
||||
#endif
|
||||
#if ULAB_SCIPY_SPECIAL_HAS_ERFC
|
||||
{ MP_ROM_QSTR(MP_QSTR_erfc), MP_ROM_PTR(&vector_erfc_obj) },
|
||||
#endif
|
||||
#if ULAB_SCIPY_SPECIAL_HAS_GAMMA
|
||||
{ MP_ROM_QSTR(MP_QSTR_gamma), MP_ROM_PTR(&vector_gamma_obj) },
|
||||
#endif
|
||||
#if ULAB_SCIPY_SPECIAL_HAS_GAMMALN
|
||||
{ MP_ROM_QSTR(MP_QSTR_gammaln), MP_ROM_PTR(&vector_lgamma_obj) },
|
||||
#endif
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_special_globals, ulab_scipy_special_globals_table);
|
||||
|
||||
const mp_obj_module_t ulab_scipy_special_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_special_globals,
|
||||
};
|
||||
#if CIRCUITPY_ULAB
|
||||
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_scipy_dot_special, ulab_scipy_special_module);
|
||||
#endif
|
||||
21
code/scipy/special/special.h
Normal file
21
code/scipy/special/special.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2021 Zoltán Vörös
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SCIPY_SPECIAL_
|
||||
#define _SCIPY_SPECIAL_
|
||||
|
||||
#include "../../ulab.h"
|
||||
#include "../../ndarray.h"
|
||||
|
||||
extern const mp_obj_module_t ulab_scipy_special_module;
|
||||
|
||||
#endif /* _SCIPY_SPECIAL_ */
|
||||
329
code/ulab.c
329
code/ulab.c
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
|
|
@ -5,7 +6,8 @@
|
|||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Zoltán Vörös
|
||||
* Copyright (c) 2019-2021 Zoltán Vörös
|
||||
* 2020 Jeff Epler for Adafruit Industries
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
|
@ -15,173 +17,218 @@
|
|||
#include "py/runtime.h"
|
||||
#include "py/binary.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/objarray.h" // this can in the future be dropped
|
||||
#include "py/objarray.h"
|
||||
|
||||
#include "ulab.h"
|
||||
#include "ndarray.h"
|
||||
#include "linalg.h"
|
||||
#include "vectorise.h"
|
||||
#include "poly.h"
|
||||
#include "fft.h"
|
||||
#include "numerical.h"
|
||||
#include "ndarray_properties.h"
|
||||
#include "numpy/create.h"
|
||||
#include "numpy/ndarray/ndarray_iter.h"
|
||||
|
||||
#define ULAB_VERSION 0.24
|
||||
#include "numpy/numpy.h"
|
||||
#include "scipy/scipy.h"
|
||||
// TODO: we should get rid of this; array.sort depends on it
|
||||
#include "numpy/numerical.h"
|
||||
|
||||
typedef struct _mp_obj_float_t {
|
||||
mp_obj_base_t base;
|
||||
mp_float_t value;
|
||||
} mp_obj_float_t;
|
||||
#include "user/user.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
mp_obj_float_t ulab_version = {{&mp_type_float}, ULAB_VERSION};
|
||||
#define ULAB_VERSION 6.9.0
|
||||
#define xstr(s) str(s)
|
||||
#define str(s) #s
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_shape_obj, ndarray_shape);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_rawsize_obj, ndarray_rawsize);
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(ndarray_flatten_obj, 1, ndarray_flatten);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_asbytearray_obj, ndarray_asbytearray);
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
#define ULAB_VERSION_STRING xstr(ULAB_VERSION) xstr(-) xstr(ULAB_MAX_DIMS) xstr(D-c)
|
||||
#else
|
||||
#define ULAB_VERSION_STRING xstr(ULAB_VERSION) xstr(-) xstr(ULAB_MAX_DIMS) xstr(D)
|
||||
#endif
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(linalg_transpose_obj, linalg_transpose);
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(linalg_reshape_obj, linalg_reshape);
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(linalg_size_obj, 1, linalg_size);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(linalg_inv_obj, linalg_inv);
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(linalg_dot_obj, linalg_dot);
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(linalg_zeros_obj, 0, linalg_zeros);
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(linalg_ones_obj, 0, linalg_ones);
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(linalg_eye_obj, 0, linalg_eye);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(linalg_det_obj, linalg_det);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(linalg_eig_obj, linalg_eig);
|
||||
static MP_DEFINE_STR_OBJ(ulab_version_obj, ULAB_VERSION_STRING);
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_acos_obj, vectorise_acos);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_acosh_obj, vectorise_acosh);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_asin_obj, vectorise_asin);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_asinh_obj, vectorise_asinh);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_atan_obj, vectorise_atan);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_atanh_obj, vectorise_atanh);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_ceil_obj, vectorise_ceil);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_cos_obj, vectorise_cos);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_erf_obj, vectorise_erf);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_erfc_obj, vectorise_erfc);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_exp_obj, vectorise_exp);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_expm1_obj, vectorise_expm1);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_floor_obj, vectorise_floor);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_gamma_obj, vectorise_gamma);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_lgamma_obj, vectorise_lgamma);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_log_obj, vectorise_log);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_log10_obj, vectorise_log10);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_log2_obj, vectorise_log2);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_sin_obj, vectorise_sin);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_sinh_obj, vectorise_sinh);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_sqrt_obj, vectorise_sqrt);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_tan_obj, vectorise_tan);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(vectorise_tanh_obj, vectorise_tanh);
|
||||
#ifdef ULAB_HASH
|
||||
static MP_DEFINE_STR_OBJ(ulab_sha_obj, xstr(ULAB_HASH));
|
||||
#endif
|
||||
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_linspace_obj, 2, numerical_linspace);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_sum_obj, 1, numerical_sum);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_mean_obj, 1, numerical_mean);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_std_obj, 1, numerical_std);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_min_obj, 1, numerical_min);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_max_obj, 1, numerical_max);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_argmin_obj, 1, numerical_argmin);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_argmax_obj, 1, numerical_argmax);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_roll_obj, 2, numerical_roll);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_flip_obj, 1, numerical_flip);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_diff_obj, 1, numerical_diff);
|
||||
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(poly_polyval_obj, poly_polyval);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poly_polyfit_obj, 2, 3, poly_polyfit);
|
||||
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_fft_obj, 1, 2, fft_fft);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_ifft_obj, 1, 2, fft_ifft);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_spectrum_obj, 1, 2, fft_spectrum);
|
||||
|
||||
STATIC const mp_rom_map_elem_t ulab_ndarray_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_shape), MP_ROM_PTR(&ndarray_shape_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_rawsize), MP_ROM_PTR(&ndarray_rawsize_obj) },
|
||||
static const mp_rom_map_elem_t ulab_ndarray_locals_dict_table[] = {
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
#if NDARRAY_HAS_RESHAPE
|
||||
{ MP_ROM_QSTR(MP_QSTR_reshape), MP_ROM_PTR(&ndarray_reshape_obj) },
|
||||
#endif
|
||||
#if NDARRAY_HAS_TRANSPOSE
|
||||
{ MP_ROM_QSTR(MP_QSTR_transpose), MP_ROM_PTR(&ndarray_transpose_obj) },
|
||||
#endif
|
||||
#endif
|
||||
#if NDARRAY_HAS_BYTESWAP
|
||||
{ MP_ROM_QSTR(MP_QSTR_byteswap), MP_ROM_PTR(&ndarray_byteswap_obj) },
|
||||
#endif
|
||||
#if NDARRAY_HAS_COPY
|
||||
{ MP_ROM_QSTR(MP_QSTR_copy), MP_ROM_PTR(&ndarray_copy_obj) },
|
||||
#endif
|
||||
#if NDARRAY_HAS_FLATTEN
|
||||
{ MP_ROM_QSTR(MP_QSTR_flatten), MP_ROM_PTR(&ndarray_flatten_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_asbytearray), MP_ROM_PTR(&ndarray_asbytearray_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_transpose), MP_ROM_PTR(&linalg_transpose_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_reshape), MP_ROM_PTR(&linalg_reshape_obj) },
|
||||
#endif
|
||||
#if NDARRAY_HAS_TOBYTES
|
||||
{ MP_ROM_QSTR(MP_QSTR_tobytes), MP_ROM_PTR(&ndarray_tobytes_obj) },
|
||||
#endif
|
||||
#if NDARRAY_HAS_TOLIST
|
||||
{ MP_ROM_QSTR(MP_QSTR_tolist), MP_ROM_PTR(&ndarray_tolist_obj) },
|
||||
#endif
|
||||
#if NDARRAY_HAS_SORT
|
||||
{ MP_ROM_QSTR(MP_QSTR_sort), MP_ROM_PTR(&numerical_sort_inplace_obj) },
|
||||
#endif
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(ulab_ndarray_locals_dict, ulab_ndarray_locals_dict_table);
|
||||
static MP_DEFINE_CONST_DICT(ulab_ndarray_locals_dict, ulab_ndarray_locals_dict_table);
|
||||
|
||||
#if defined(MP_DEFINE_CONST_OBJ_TYPE)
|
||||
// MicroPython after-b41aaaa (Sept 19 2022).
|
||||
|
||||
#if NDARRAY_IS_SLICEABLE
|
||||
#define NDARRAY_TYPE_SUBSCR subscr, ndarray_subscr,
|
||||
#else
|
||||
#define NDARRAY_TYPE_SUBSCR
|
||||
#endif
|
||||
#if NDARRAY_IS_ITERABLE
|
||||
#define NDARRAY_TYPE_ITER iter, ndarray_getiter,
|
||||
#define NDARRAY_TYPE_ITER_FLAGS MP_TYPE_FLAG_ITER_IS_GETITER
|
||||
#else
|
||||
#define NDARRAY_TYPE_ITER
|
||||
#define NDARRAY_TYPE_ITER_FLAGS 0
|
||||
#endif
|
||||
#if NDARRAY_HAS_UNARY_OPS
|
||||
#define NDARRAY_TYPE_UNARY_OP unary_op, ndarray_unary_op,
|
||||
#else
|
||||
#define NDARRAY_TYPE_UNARY_OP
|
||||
#endif
|
||||
#if NDARRAY_HAS_BINARY_OPS
|
||||
#define NDARRAY_TYPE_BINARY_OP binary_op, ndarray_binary_op,
|
||||
#else
|
||||
#define NDARRAY_TYPE_BINARY_OP
|
||||
#endif
|
||||
|
||||
MP_DEFINE_CONST_OBJ_TYPE(
|
||||
ulab_ndarray_type,
|
||||
MP_QSTR_ndarray,
|
||||
MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_EQ_HAS_NEQ_TEST | NDARRAY_TYPE_ITER_FLAGS,
|
||||
print, ndarray_print,
|
||||
make_new, ndarray_make_new,
|
||||
locals_dict, &ulab_ndarray_locals_dict,
|
||||
NDARRAY_TYPE_SUBSCR
|
||||
NDARRAY_TYPE_ITER
|
||||
NDARRAY_TYPE_UNARY_OP
|
||||
NDARRAY_TYPE_BINARY_OP
|
||||
attr, ndarray_properties_attr,
|
||||
buffer, ndarray_get_buffer
|
||||
);
|
||||
|
||||
#else
|
||||
// CircuitPython and earlier MicroPython revisions.
|
||||
const mp_obj_type_t ulab_ndarray_type = {
|
||||
{ &mp_type_type },
|
||||
.flags = MP_TYPE_FLAG_EXTENDED
|
||||
#if defined(MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE) && defined(MP_TYPE_FLAG_EQ_HAS_NEQ_TEST)
|
||||
| MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_EQ_HAS_NEQ_TEST
|
||||
#endif
|
||||
,
|
||||
.name = MP_QSTR_ndarray,
|
||||
.print = ndarray_print,
|
||||
.make_new = ndarray_make_new,
|
||||
.subscr = ndarray_subscr,
|
||||
.getiter = ndarray_getiter,
|
||||
.unary_op = ndarray_unary_op,
|
||||
.binary_op = ndarray_binary_op,
|
||||
.locals_dict = (mp_obj_dict_t*)&ulab_ndarray_locals_dict,
|
||||
MP_TYPE_EXTENDED_FIELDS(
|
||||
#if NDARRAY_IS_SLICEABLE
|
||||
.subscr = ndarray_subscr,
|
||||
#endif
|
||||
#if NDARRAY_IS_ITERABLE
|
||||
.getiter = ndarray_getiter,
|
||||
#endif
|
||||
#if NDARRAY_HAS_UNARY_OPS
|
||||
.unary_op = ndarray_unary_op,
|
||||
#endif
|
||||
#if NDARRAY_HAS_BINARY_OPS
|
||||
.binary_op = ndarray_binary_op,
|
||||
#endif
|
||||
.attr = ndarray_properties_attr,
|
||||
.buffer_p = { .get_buffer = ndarray_get_buffer, },
|
||||
)
|
||||
};
|
||||
#endif
|
||||
|
||||
#if ULAB_HAS_DTYPE_OBJECT
|
||||
|
||||
#if defined(MP_DEFINE_CONST_OBJ_TYPE)
|
||||
MP_DEFINE_CONST_OBJ_TYPE(
|
||||
ulab_dtype_type,
|
||||
MP_QSTR_dtype,
|
||||
MP_TYPE_FLAG_NONE,
|
||||
print, ndarray_dtype_print,
|
||||
make_new, ndarray_dtype_make_new
|
||||
);
|
||||
#else
|
||||
const mp_obj_type_t ulab_dtype_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_dtype,
|
||||
.print = ndarray_dtype_print,
|
||||
.make_new = ndarray_dtype_make_new,
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if NDARRAY_HAS_FLATITER
|
||||
#if defined(MP_DEFINE_CONST_OBJ_TYPE)
|
||||
MP_DEFINE_CONST_OBJ_TYPE(
|
||||
ndarray_flatiter_type,
|
||||
MP_QSTR_flatiter,
|
||||
MP_TYPE_FLAG_ITER_IS_GETITER,
|
||||
iter, ndarray_get_flatiterator
|
||||
);
|
||||
#else
|
||||
const mp_obj_type_t ndarray_flatiter_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_flatiter,
|
||||
MP_TYPE_EXTENDED_FIELDS(
|
||||
.getiter = ndarray_get_flatiterator,
|
||||
)
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static const mp_rom_map_elem_t ulab_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ulab) },
|
||||
{ MP_ROM_QSTR(MP_QSTR___version__), MP_ROM_PTR(&ulab_version_obj) },
|
||||
#ifdef ULAB_HASH
|
||||
{ MP_ROM_QSTR(MP_QSTR___sha__), MP_ROM_PTR(&ulab_sha_obj) },
|
||||
#endif
|
||||
#if ULAB_HAS_DTYPE_OBJECT
|
||||
{ MP_ROM_QSTR(MP_QSTR_dtype), MP_ROM_PTR(&ulab_dtype_type) },
|
||||
#else
|
||||
#if NDARRAY_HAS_DTYPE
|
||||
{ MP_ROM_QSTR(MP_QSTR_dtype), MP_ROM_PTR(&ndarray_dtype_obj) },
|
||||
#endif /* NDARRAY_HAS_DTYPE */
|
||||
#endif /* ULAB_HAS_DTYPE_OBJECT */
|
||||
{ MP_ROM_QSTR(MP_QSTR_numpy), MP_ROM_PTR(&ulab_numpy_module) },
|
||||
#if ULAB_HAS_SCIPY
|
||||
{ MP_ROM_QSTR(MP_QSTR_scipy), MP_ROM_PTR(&ulab_scipy_module) },
|
||||
#endif
|
||||
#if ULAB_HAS_USER_MODULE
|
||||
{ MP_ROM_QSTR(MP_QSTR_user), MP_ROM_PTR(&ulab_user_module) },
|
||||
#endif
|
||||
#if ULAB_HAS_UTILS_MODULE
|
||||
{ MP_ROM_QSTR(MP_QSTR_utils), MP_ROM_PTR(&ulab_utils_module) },
|
||||
#endif
|
||||
};
|
||||
|
||||
STATIC const mp_map_elem_t ulab_globals_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_ulab) },
|
||||
{ MP_ROM_QSTR(MP_QSTR___version__), MP_ROM_PTR(&ulab_version) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_array), (mp_obj_t)&ulab_ndarray_type },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_size), (mp_obj_t)&linalg_size_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_inv), (mp_obj_t)&linalg_inv_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_dot), (mp_obj_t)&linalg_dot_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_zeros), (mp_obj_t)&linalg_zeros_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ones), (mp_obj_t)&linalg_ones_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_eye), (mp_obj_t)&linalg_eye_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_det), (mp_obj_t)&linalg_det_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_eig), (mp_obj_t)&linalg_eig_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_acos), (mp_obj_t)&vectorise_acos_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_acosh), (mp_obj_t)&vectorise_acosh_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_asin), (mp_obj_t)&vectorise_asin_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_asinh), (mp_obj_t)&vectorise_asinh_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_atan), (mp_obj_t)&vectorise_atan_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_atanh), (mp_obj_t)&vectorise_atanh_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_ceil), (mp_obj_t)&vectorise_ceil_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_cos), (mp_obj_t)&vectorise_cos_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_erf), (mp_obj_t)&vectorise_erf_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_erfc), (mp_obj_t)&vectorise_erfc_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_exp), (mp_obj_t)&vectorise_exp_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_expm1), (mp_obj_t)&vectorise_expm1_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_floor), (mp_obj_t)&vectorise_floor_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_gamma), (mp_obj_t)&vectorise_gamma_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_lgamma), (mp_obj_t)&vectorise_lgamma_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_log), (mp_obj_t)&vectorise_log_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_log10), (mp_obj_t)&vectorise_log10_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_log2), (mp_obj_t)&vectorise_log2_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_sin), (mp_obj_t)&vectorise_sin_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_sinh), (mp_obj_t)&vectorise_sinh_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_sqrt), (mp_obj_t)&vectorise_sqrt_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_tan), (mp_obj_t)&vectorise_tan_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_tanh), (mp_obj_t)&vectorise_tanh_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_linspace), (mp_obj_t)&numerical_linspace_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_sum), (mp_obj_t)&numerical_sum_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_mean), (mp_obj_t)&numerical_mean_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_std), (mp_obj_t)&numerical_std_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_min), (mp_obj_t)&numerical_min_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_max), (mp_obj_t)&numerical_max_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_argmin), (mp_obj_t)&numerical_argmin_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_argmax), (mp_obj_t)&numerical_argmax_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_roll), (mp_obj_t)&numerical_roll_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_flip), (mp_obj_t)&numerical_flip_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_diff), (mp_obj_t)&numerical_diff_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_polyval), (mp_obj_t)&poly_polyval_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_polyfit), (mp_obj_t)&poly_polyfit_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_fft), (mp_obj_t)&fft_fft_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_ifft), (mp_obj_t)&fft_ifft_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_spectrum), (mp_obj_t)&fft_spectrum_obj },
|
||||
// class constants
|
||||
{ MP_ROM_QSTR(MP_QSTR_uint8), MP_ROM_INT(NDARRAY_UINT8) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_int8), MP_ROM_INT(NDARRAY_INT8) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_uint16), MP_ROM_INT(NDARRAY_UINT16) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_int16), MP_ROM_INT(NDARRAY_INT16) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_float), MP_ROM_INT(NDARRAY_FLOAT) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT (
|
||||
static MP_DEFINE_CONST_DICT (
|
||||
mp_module_ulab_globals,
|
||||
ulab_globals_table
|
||||
);
|
||||
|
||||
#ifdef OPENMV
|
||||
const struct _mp_obj_module_t ulab_user_cmodule = {
|
||||
#else
|
||||
const mp_obj_module_t ulab_user_cmodule = {
|
||||
#endif
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_globals,
|
||||
};
|
||||
|
||||
MP_REGISTER_MODULE(MP_QSTR_ulab, ulab_user_cmodule, MODULE_ULAB_ENABLED);
|
||||
MP_REGISTER_MODULE(MP_QSTR_ulab, ulab_user_cmodule);
|
||||
|
|
|
|||
848
code/ulab.h
Normal file
848
code/ulab.h
Normal file
|
|
@ -0,0 +1,848 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2022 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef __ULAB__
|
||||
#define __ULAB__
|
||||
|
||||
|
||||
|
||||
// The pre-processor constants in this file determine how ulab behaves:
|
||||
//
|
||||
// - how many dimensions ulab can handle
|
||||
// - which functions are included in the compiled firmware
|
||||
// - whether arrays can be sliced and iterated over
|
||||
// - which binary/unary operators are supported
|
||||
// - whether ulab can deal with complex numbers
|
||||
//
|
||||
// A considerable amount of flash space can be saved by removing (setting
|
||||
// the corresponding constants to 0) the unnecessary functions and features.
|
||||
|
||||
// Values defined here can be overridden by your own config file as
|
||||
// make -DULAB_CONFIG_FILE="my_ulab_config.h"
|
||||
#if defined(ULAB_CONFIG_FILE)
|
||||
#include ULAB_CONFIG_FILE
|
||||
#endif
|
||||
|
||||
// Adds support for complex ndarrays
|
||||
#ifndef ULAB_SUPPORTS_COMPLEX
|
||||
#define ULAB_SUPPORTS_COMPLEX (1)
|
||||
#endif
|
||||
|
||||
// Determines, whether scipy is defined in ulab. The sub-modules and functions
|
||||
// of scipy have to be defined separately
|
||||
#ifndef ULAB_HAS_SCIPY
|
||||
#define ULAB_HAS_SCIPY (1)
|
||||
#endif
|
||||
|
||||
// The maximum number of dimensions the firmware should be able to support
|
||||
// Possible values lie between 1, and 4, inclusive
|
||||
#ifndef ULAB_MAX_DIMS
|
||||
#define ULAB_MAX_DIMS 2
|
||||
#endif
|
||||
|
||||
// By setting this constant to 1, iteration over array dimensions will be implemented
|
||||
// as a function (ndarray_rewind_array), instead of writing out the loops in macros
|
||||
// This reduces firmware size at the expense of speed
|
||||
#ifndef ULAB_HAS_FUNCTION_ITERATOR
|
||||
#define ULAB_HAS_FUNCTION_ITERATOR (0)
|
||||
#endif
|
||||
|
||||
// If NDARRAY_IS_ITERABLE is 1, the ndarray object defines its own iterator function
|
||||
// This option saves approx. 250 bytes of flash space
|
||||
#ifndef NDARRAY_IS_ITERABLE
|
||||
#define NDARRAY_IS_ITERABLE (1)
|
||||
#endif
|
||||
|
||||
// Slicing can be switched off by setting this variable to 0
|
||||
#ifndef NDARRAY_IS_SLICEABLE
|
||||
#define NDARRAY_IS_SLICEABLE (1)
|
||||
#endif
|
||||
|
||||
// The default threshold for pretty printing. These variables can be overwritten
|
||||
// at run-time via the set_printoptions() function
|
||||
#ifndef ULAB_HAS_PRINTOPTIONS
|
||||
#define ULAB_HAS_PRINTOPTIONS (1)
|
||||
#endif
|
||||
#define NDARRAY_PRINT_THRESHOLD 10
|
||||
#define NDARRAY_PRINT_EDGEITEMS 3
|
||||
|
||||
// determines, whether the dtype is an object, or simply a character
|
||||
// the object implementation is numpythonic, but requires more space
|
||||
#ifndef ULAB_HAS_DTYPE_OBJECT
|
||||
#define ULAB_HAS_DTYPE_OBJECT (0)
|
||||
#endif
|
||||
|
||||
// the ndarray binary operators
|
||||
#ifndef NDARRAY_HAS_BINARY_OPS
|
||||
#define NDARRAY_HAS_BINARY_OPS (1)
|
||||
#endif
|
||||
|
||||
// Firmware size can be reduced at the expense of speed by using function
|
||||
// pointers in iterations. For each operator, he function pointer saves around
|
||||
// 2 kB in the two-dimensional case, and around 4 kB in the four-dimensional case.
|
||||
|
||||
#ifndef NDARRAY_BINARY_USES_FUN_POINTER
|
||||
#define NDARRAY_BINARY_USES_FUN_POINTER (0)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_BINARY_OP_ADD
|
||||
#define NDARRAY_HAS_BINARY_OP_ADD (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_BINARY_OP_AND
|
||||
#define NDARRAY_HAS_BINARY_OP_AND (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_BINARY_OP_EQUAL
|
||||
#define NDARRAY_HAS_BINARY_OP_EQUAL (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_BINARY_OP_FLOOR_DIVIDE
|
||||
#define NDARRAY_HAS_BINARY_OP_FLOOR_DIVIDE (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_BINARY_OP_LESS
|
||||
#define NDARRAY_HAS_BINARY_OP_LESS (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_BINARY_OP_LESS_EQUAL
|
||||
#define NDARRAY_HAS_BINARY_OP_LESS_EQUAL (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_BINARY_OP_MODULO
|
||||
#define NDARRAY_HAS_BINARY_OP_MODULO (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_BINARY_OP_MORE
|
||||
#define NDARRAY_HAS_BINARY_OP_MORE (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_BINARY_OP_MORE_EQUAL
|
||||
#define NDARRAY_HAS_BINARY_OP_MORE_EQUAL (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_BINARY_OP_MULTIPLY
|
||||
#define NDARRAY_HAS_BINARY_OP_MULTIPLY (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_BINARY_OP_NOT_EQUAL
|
||||
#define NDARRAY_HAS_BINARY_OP_NOT_EQUAL (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_BINARY_OP_OR
|
||||
#define NDARRAY_HAS_BINARY_OP_OR (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_BINARY_OP_POWER
|
||||
#define NDARRAY_HAS_BINARY_OP_POWER (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_BINARY_OP_SUBTRACT
|
||||
#define NDARRAY_HAS_BINARY_OP_SUBTRACT (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_BINARY_OP_TRUE_DIVIDE
|
||||
#define NDARRAY_HAS_BINARY_OP_TRUE_DIVIDE (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_BINARY_OP_XOR
|
||||
#define NDARRAY_HAS_BINARY_OP_XOR (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_INPLACE_OPS
|
||||
#define NDARRAY_HAS_INPLACE_OPS (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_INPLACE_ADD
|
||||
#define NDARRAY_HAS_INPLACE_ADD (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_INPLACE_MODULO
|
||||
#define NDARRAY_HAS_INPLACE_MODU (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_INPLACE_MULTIPLY
|
||||
#define NDARRAY_HAS_INPLACE_MULTIPLY (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_INPLACE_POWER
|
||||
#define NDARRAY_HAS_INPLACE_POWER (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_INPLACE_SUBTRACT
|
||||
#define NDARRAY_HAS_INPLACE_SUBTRACT (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_INPLACE_TRUE_DIVIDE
|
||||
#define NDARRAY_HAS_INPLACE_TRUE_DIVIDE (1)
|
||||
#endif
|
||||
|
||||
// bitwise operators
|
||||
#ifndef ULAB_NUMPY_HAS_BITWISE_AND
|
||||
#define ULAB_NUMPY_HAS_BITWISE_AND (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_BITWISE_OR
|
||||
#define ULAB_NUMPY_HAS_BITWISE_OR (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_BITWISE_XOR
|
||||
#define ULAB_NUMPY_HAS_BITWISE_XOR (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_LEFT_SHIFT
|
||||
#define ULAB_NUMPY_HAS_LEFT_SHIFT (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_RIGHT_SHIFT
|
||||
#define ULAB_NUMPY_HAS_RIGHT_SHIFT (1)
|
||||
#endif
|
||||
|
||||
// the ndarray unary operators
|
||||
#ifndef NDARRAY_HAS_UNARY_OPS
|
||||
#define NDARRAY_HAS_UNARY_OPS (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_UNARY_OP_ABS
|
||||
#define NDARRAY_HAS_UNARY_OP_ABS (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_UNARY_OP_INVERT
|
||||
#define NDARRAY_HAS_UNARY_OP_INVERT (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_UNARY_OP_LEN
|
||||
#define NDARRAY_HAS_UNARY_OP_LEN (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_UNARY_OP_NEGATIVE
|
||||
#define NDARRAY_HAS_UNARY_OP_NEGATIVE (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_UNARY_OP_POSITIVE
|
||||
#define NDARRAY_HAS_UNARY_OP_POSITIVE (1)
|
||||
#endif
|
||||
|
||||
|
||||
// determines, which ndarray methods are available
|
||||
#ifndef NDARRAY_HAS_BYTESWAP
|
||||
#define NDARRAY_HAS_BYTESWAP (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_COPY
|
||||
#define NDARRAY_HAS_COPY (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_DTYPE
|
||||
#define NDARRAY_HAS_DTYPE (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_FLATTEN
|
||||
#define NDARRAY_HAS_FLATTEN (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_ITEMSIZE
|
||||
#define NDARRAY_HAS_ITEMSIZE (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_NDIM
|
||||
#define NDARRAY_HAS_NDIM (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_RESHAPE
|
||||
#define NDARRAY_HAS_RESHAPE (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_SHAPE
|
||||
#define NDARRAY_HAS_SHAPE (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_SIZE
|
||||
#define NDARRAY_HAS_SIZE (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_SORT
|
||||
#define NDARRAY_HAS_SORT (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_STRIDES
|
||||
#define NDARRAY_HAS_STRIDES (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_TOBYTES
|
||||
#define NDARRAY_HAS_TOBYTES (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_TOLIST
|
||||
#define NDARRAY_HAS_TOLIST (1)
|
||||
#endif
|
||||
|
||||
#ifndef NDARRAY_HAS_TRANSPOSE
|
||||
#define NDARRAY_HAS_TRANSPOSE (1)
|
||||
#endif
|
||||
|
||||
// Firmware size can be reduced at the expense of speed by using a function
|
||||
// pointer in iterations. Setting ULAB_VECTORISE_USES_FUNCPOINTER to 1 saves
|
||||
// around 800 bytes in the four-dimensional case, and around 200 in two dimensions.
|
||||
#ifndef ULAB_VECTORISE_USES_FUN_POINTER
|
||||
#define ULAB_VECTORISE_USES_FUN_POINTER (1)
|
||||
#endif
|
||||
|
||||
// determines, whether e is defined in ulab.numpy itself
|
||||
#ifndef ULAB_NUMPY_HAS_E
|
||||
#define ULAB_NUMPY_HAS_E (1)
|
||||
#endif
|
||||
|
||||
// ulab defines infinite as a class constant in ulab.numpy
|
||||
#ifndef ULAB_NUMPY_HAS_INF
|
||||
#define ULAB_NUMPY_HAS_INF (1)
|
||||
#endif
|
||||
|
||||
// ulab defines NaN as a class constant in ulab.numpy
|
||||
#ifndef ULAB_NUMPY_HAS_NAN
|
||||
#define ULAB_NUMPY_HAS_NAN (1)
|
||||
#endif
|
||||
|
||||
// determines, whether pi is defined in ulab.numpy itself
|
||||
#ifndef ULAB_NUMPY_HAS_PI
|
||||
#define ULAB_NUMPY_HAS_PI (1)
|
||||
#endif
|
||||
|
||||
// determines, whether the ndinfo function is available
|
||||
#ifndef ULAB_NUMPY_HAS_NDINFO
|
||||
#define ULAB_NUMPY_HAS_NDINFO (1)
|
||||
#endif
|
||||
|
||||
// if this constant is set to 1, the interpreter can iterate
|
||||
// over the flat array without copying any data
|
||||
#ifndef NDARRAY_HAS_FLATITER
|
||||
#define NDARRAY_HAS_FLATITER (1)
|
||||
#endif
|
||||
|
||||
// frombuffer adds 600 bytes to the firmware
|
||||
#ifndef ULAB_NUMPY_HAS_FROMBUFFER
|
||||
#define ULAB_NUMPY_HAS_FROMBUFFER (1)
|
||||
#endif
|
||||
|
||||
// functions that create an array
|
||||
#ifndef ULAB_NUMPY_HAS_ARANGE
|
||||
#define ULAB_NUMPY_HAS_ARANGE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_CONCATENATE
|
||||
#define ULAB_NUMPY_HAS_CONCATENATE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_DIAG
|
||||
#define ULAB_NUMPY_HAS_DIAG (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_EMPTY
|
||||
#define ULAB_NUMPY_HAS_EMPTY (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_EYE
|
||||
#define ULAB_NUMPY_HAS_EYE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_FULL
|
||||
#define ULAB_NUMPY_HAS_FULL (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_LINSPACE
|
||||
#define ULAB_NUMPY_HAS_LINSPACE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_LOGSPACE
|
||||
#define ULAB_NUMPY_HAS_LOGSPACE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_ONES
|
||||
#define ULAB_NUMPY_HAS_ONES (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_ZEROS
|
||||
#define ULAB_NUMPY_HAS_ZEROS (1)
|
||||
#endif
|
||||
|
||||
// functions that compare arrays
|
||||
#ifndef ULAB_NUMPY_HAS_CLIP
|
||||
#define ULAB_NUMPY_HAS_CLIP (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_EQUAL
|
||||
#define ULAB_NUMPY_HAS_EQUAL (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_ISFINITE
|
||||
#define ULAB_NUMPY_HAS_ISFINITE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_ISINF
|
||||
#define ULAB_NUMPY_HAS_ISINF (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_MAXIMUM
|
||||
#define ULAB_NUMPY_HAS_MAXIMUM (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_MINIMUM
|
||||
#define ULAB_NUMPY_HAS_MINIMUM (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_NONZERO
|
||||
#define ULAB_NUMPY_HAS_NONZERO (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_NOTEQUAL
|
||||
#define ULAB_NUMPY_HAS_NOTEQUAL (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_WHERE
|
||||
#define ULAB_NUMPY_HAS_WHERE (1)
|
||||
#endif
|
||||
|
||||
// the integrate module; functions of the integrate module still have
|
||||
// to be defined separately
|
||||
#ifndef ULAB_SCIPY_HAS_INTEGRATE_MODULE
|
||||
#define ULAB_SCIPY_HAS_INTEGRATE_MODULE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_INTEGRATE_HAS_TANHSINH
|
||||
#define ULAB_INTEGRATE_HAS_TANHSINH (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_INTEGRATE_HAS_ROMBERG
|
||||
#define ULAB_INTEGRATE_HAS_ROMBERG (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_INTEGRATE_HAS_SIMPSON
|
||||
#define ULAB_INTEGRATE_HAS_SIMPSON (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_INTEGRATE_HAS_QUAD
|
||||
#define ULAB_INTEGRATE_HAS_QUAD (1)
|
||||
#endif
|
||||
|
||||
// the linalg module; functions of the linalg module still have
|
||||
// to be defined separately
|
||||
#ifndef ULAB_NUMPY_HAS_LINALG_MODULE
|
||||
#define ULAB_NUMPY_HAS_LINALG_MODULE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_LINALG_HAS_CHOLESKY
|
||||
#define ULAB_LINALG_HAS_CHOLESKY (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_LINALG_HAS_DET
|
||||
#define ULAB_LINALG_HAS_DET (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_LINALG_HAS_EIG
|
||||
#define ULAB_LINALG_HAS_EIG (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_LINALG_HAS_INV
|
||||
#define ULAB_LINALG_HAS_INV (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_LINALG_HAS_NORM
|
||||
#define ULAB_LINALG_HAS_NORM (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_LINALG_HAS_QR
|
||||
#define ULAB_LINALG_HAS_QR (1)
|
||||
#endif
|
||||
|
||||
// the FFT module; functions of the fft module still have
|
||||
// to be defined separately
|
||||
#ifndef ULAB_NUMPY_HAS_FFT_MODULE
|
||||
#define ULAB_NUMPY_HAS_FFT_MODULE (1)
|
||||
#endif
|
||||
|
||||
// By setting this constant to 1, the FFT routine will behave in a
|
||||
// numpy-compatible way, i.e., it will output a complex array
|
||||
// This setting has no effect, if ULAB_SUPPORTS_COMPLEX is 0
|
||||
// Note that in this case, the input also must be numpythonic,
|
||||
// i.e., the real an imaginary parts cannot be passed as two arguments
|
||||
#ifndef ULAB_FFT_IS_NUMPY_COMPATIBLE
|
||||
#define ULAB_FFT_IS_NUMPY_COMPATIBLE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_FFT_HAS_FFT
|
||||
#define ULAB_FFT_HAS_FFT (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_FFT_HAS_IFFT
|
||||
#define ULAB_FFT_HAS_IFFT (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_ALL
|
||||
#define ULAB_NUMPY_HAS_ALL (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_ANY
|
||||
#define ULAB_NUMPY_HAS_ANY (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_ARGMINMAX
|
||||
#define ULAB_NUMPY_HAS_ARGMINMAX (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_ARGSORT
|
||||
#define ULAB_NUMPY_HAS_ARGSORT (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_ASARRAY
|
||||
#define ULAB_NUMPY_HAS_ASARRAY (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_COMPRESS
|
||||
#define ULAB_NUMPY_HAS_COMPRESS (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_CONVOLVE
|
||||
#define ULAB_NUMPY_HAS_CONVOLVE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_CROSS
|
||||
#define ULAB_NUMPY_HAS_CROSS (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_DELETE
|
||||
#define ULAB_NUMPY_HAS_DELETE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_DIFF
|
||||
#define ULAB_NUMPY_HAS_DIFF (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_DOT
|
||||
#define ULAB_NUMPY_HAS_DOT (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_FLIP
|
||||
#define ULAB_NUMPY_HAS_FLIP (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_INTERP
|
||||
#define ULAB_NUMPY_HAS_INTERP (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_LOAD
|
||||
#define ULAB_NUMPY_HAS_LOAD (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_LOADTXT
|
||||
#define ULAB_NUMPY_HAS_LOADTXT (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_MEAN
|
||||
#define ULAB_NUMPY_HAS_MEAN (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_MEDIAN
|
||||
#define ULAB_NUMPY_HAS_MEDIAN (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_MINMAX
|
||||
#define ULAB_NUMPY_HAS_MINMAX (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_POLYFIT
|
||||
#define ULAB_NUMPY_HAS_POLYFIT (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_POLYVAL
|
||||
#define ULAB_NUMPY_HAS_POLYVAL (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_ROLL
|
||||
#define ULAB_NUMPY_HAS_ROLL (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_SAVE
|
||||
#define ULAB_NUMPY_HAS_SAVE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_SAVETXT
|
||||
#define ULAB_NUMPY_HAS_SAVETXT (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_SIZE
|
||||
#define ULAB_NUMPY_HAS_SIZE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_SORT
|
||||
#define ULAB_NUMPY_HAS_SORT (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_STD
|
||||
#define ULAB_NUMPY_HAS_STD (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_SUM
|
||||
#define ULAB_NUMPY_HAS_SUM (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_TAKE
|
||||
#define ULAB_NUMPY_HAS_TAKE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_TRACE
|
||||
#define ULAB_NUMPY_HAS_TRACE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_TRAPZ
|
||||
#define ULAB_NUMPY_HAS_TRAPZ (1)
|
||||
#endif
|
||||
|
||||
// vectorised versions of the functions of the math python module, with
|
||||
// the exception of the functions listed in scipy.special
|
||||
|
||||
// if this constant is set, math functions support the out keyword argument
|
||||
#ifndef ULAB_MATH_FUNCTIONS_OUT_KEYWORD
|
||||
#define ULAB_MATH_FUNCTIONS_OUT_KEYWORD (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_ACOS
|
||||
#define ULAB_NUMPY_HAS_ACOS (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_ACOSH
|
||||
#define ULAB_NUMPY_HAS_ACOSH (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_ARCTAN2
|
||||
#define ULAB_NUMPY_HAS_ARCTAN2 (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_AROUND
|
||||
#define ULAB_NUMPY_HAS_AROUND (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_ASIN
|
||||
#define ULAB_NUMPY_HAS_ASIN (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_ASINH
|
||||
#define ULAB_NUMPY_HAS_ASINH (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_ATAN
|
||||
#define ULAB_NUMPY_HAS_ATAN (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_ATANH
|
||||
#define ULAB_NUMPY_HAS_ATANH (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_CEIL
|
||||
#define ULAB_NUMPY_HAS_CEIL (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_COS
|
||||
#define ULAB_NUMPY_HAS_COS (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_COSH
|
||||
#define ULAB_NUMPY_HAS_COSH (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_DEGREES
|
||||
#define ULAB_NUMPY_HAS_DEGREES (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_EXP
|
||||
#define ULAB_NUMPY_HAS_EXP (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_EXPM1
|
||||
#define ULAB_NUMPY_HAS_EXPM1 (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_FLOOR
|
||||
#define ULAB_NUMPY_HAS_FLOOR (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_LOG
|
||||
#define ULAB_NUMPY_HAS_LOG (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_LOG10
|
||||
#define ULAB_NUMPY_HAS_LOG10 (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_LOG2
|
||||
#define ULAB_NUMPY_HAS_LOG2 (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_RADIANS
|
||||
#define ULAB_NUMPY_HAS_RADIANS (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_SIN
|
||||
#define ULAB_NUMPY_HAS_SIN (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_SINC
|
||||
#define ULAB_NUMPY_HAS_SINC (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_SINH
|
||||
#define ULAB_NUMPY_HAS_SINH (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_SQRT
|
||||
#define ULAB_NUMPY_HAS_SQRT (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_TAN
|
||||
#define ULAB_NUMPY_HAS_TAN (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_TANH
|
||||
#define ULAB_NUMPY_HAS_TANH (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_VECTORIZE
|
||||
#define ULAB_NUMPY_HAS_VECTORIZE (1)
|
||||
#endif
|
||||
|
||||
// Complex functions. The implementations are compiled into
|
||||
// the firmware, only if ULAB_SUPPORTS_COMPLEX is set to 1
|
||||
#ifndef ULAB_NUMPY_HAS_CONJUGATE
|
||||
#define ULAB_NUMPY_HAS_CONJUGATE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_IMAG
|
||||
#define ULAB_NUMPY_HAS_IMAG (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_REAL
|
||||
#define ULAB_NUMPY_HAS_REAL (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_HAS_SORT_COMPLEX
|
||||
#define ULAB_NUMPY_HAS_SORT_COMPLEX (1)
|
||||
#endif
|
||||
|
||||
// random module
|
||||
#ifndef ULAB_NUMPY_HAS_RANDOM_MODULE
|
||||
#define ULAB_NUMPY_HAS_RANDOM_MODULE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_RANDOM_HAS_NORMAL
|
||||
#define ULAB_NUMPY_RANDOM_HAS_NORMAL (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_RANDOM_HAS_RANDOM
|
||||
#define ULAB_NUMPY_RANDOM_HAS_RANDOM (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_NUMPY_RANDOM_HAS_UNIFORM
|
||||
#define ULAB_NUMPY_RANDOM_HAS_UNIFORM (1)
|
||||
#endif
|
||||
|
||||
|
||||
// scipy modules
|
||||
#ifndef ULAB_SCIPY_HAS_LINALG_MODULE
|
||||
#define ULAB_SCIPY_HAS_LINALG_MODULE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_SCIPY_LINALG_HAS_CHO_SOLVE
|
||||
#define ULAB_SCIPY_LINALG_HAS_CHO_SOLVE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_SCIPY_LINALG_HAS_SOLVE_TRIANGULAR
|
||||
#define ULAB_SCIPY_LINALG_HAS_SOLVE_TRIANGULAR (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_SCIPY_HAS_SIGNAL_MODULE
|
||||
#define ULAB_SCIPY_HAS_SIGNAL_MODULE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_SCIPY_SIGNAL_HAS_SOSFILT
|
||||
#define ULAB_SCIPY_SIGNAL_HAS_SOSFILT (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_SCIPY_HAS_OPTIMIZE_MODULE
|
||||
#define ULAB_SCIPY_HAS_OPTIMIZE_MODULE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_SCIPY_OPTIMIZE_HAS_BISECT
|
||||
#define ULAB_SCIPY_OPTIMIZE_HAS_BISECT (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_SCIPY_OPTIMIZE_HAS_CURVE_FIT
|
||||
#define ULAB_SCIPY_OPTIMIZE_HAS_CURVE_FIT (0) // not fully implemented
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_SCIPY_OPTIMIZE_HAS_FMIN
|
||||
#define ULAB_SCIPY_OPTIMIZE_HAS_FMIN (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_SCIPY_OPTIMIZE_HAS_NEWTON
|
||||
#define ULAB_SCIPY_OPTIMIZE_HAS_NEWTON (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_SCIPY_HAS_SPECIAL_MODULE
|
||||
#define ULAB_SCIPY_HAS_SPECIAL_MODULE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_SCIPY_SPECIAL_HAS_ERF
|
||||
#define ULAB_SCIPY_SPECIAL_HAS_ERF (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_SCIPY_SPECIAL_HAS_ERFC
|
||||
#define ULAB_SCIPY_SPECIAL_HAS_ERFC (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_SCIPY_SPECIAL_HAS_GAMMA
|
||||
#define ULAB_SCIPY_SPECIAL_HAS_GAMMA (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_SCIPY_SPECIAL_HAS_GAMMALN
|
||||
#define ULAB_SCIPY_SPECIAL_HAS_GAMMALN (1)
|
||||
#endif
|
||||
|
||||
// functions of the utils module
|
||||
#ifndef ULAB_HAS_UTILS_MODULE
|
||||
#define ULAB_HAS_UTILS_MODULE (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_UTILS_HAS_FROM_INT16_BUFFER
|
||||
#define ULAB_UTILS_HAS_FROM_INT16_BUFFER (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_UTILS_HAS_FROM_UINT16_BUFFER
|
||||
#define ULAB_UTILS_HAS_FROM_UINT16_BUFFER (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_UTILS_HAS_FROM_INT32_BUFFER
|
||||
#define ULAB_UTILS_HAS_FROM_INT32_BUFFER (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_UTILS_HAS_FROM_UINT32_BUFFER
|
||||
#define ULAB_UTILS_HAS_FROM_UINT32_BUFFER (1)
|
||||
#endif
|
||||
|
||||
#ifndef ULAB_UTILS_HAS_SPECTROGRAM
|
||||
#define ULAB_UTILS_HAS_SPECTROGRAM (1)
|
||||
#endif
|
||||
|
||||
// user-defined module; source of the module and
|
||||
// its sub-modules should be placed in code/user/
|
||||
#ifndef ULAB_HAS_USER_MODULE
|
||||
#define ULAB_HAS_USER_MODULE (0)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
332
code/ulab_tools.c
Normal file
332
code/ulab_tools.c
Normal file
|
|
@ -0,0 +1,332 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2022 Zoltán Vörös
|
||||
*/
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include "py/runtime.h"
|
||||
|
||||
#include "ulab.h"
|
||||
#include "ndarray.h"
|
||||
#include "ulab_tools.h"
|
||||
|
||||
// The following five functions return a float from a void type
|
||||
// The value in question is supposed to be located at the head of the pointer
|
||||
|
||||
mp_float_t ndarray_get_float_uint8(void *data) {
|
||||
// Returns a float value from an uint8_t type
|
||||
return (mp_float_t)(*(uint8_t *)data);
|
||||
}
|
||||
|
||||
mp_float_t ndarray_get_float_int8(void *data) {
|
||||
// Returns a float value from an int8_t type
|
||||
return (mp_float_t)(*(int8_t *)data);
|
||||
}
|
||||
|
||||
mp_float_t ndarray_get_float_uint16(void *data) {
|
||||
// Returns a float value from an uint16_t type
|
||||
return (mp_float_t)(*(uint16_t *)data);
|
||||
}
|
||||
|
||||
mp_float_t ndarray_get_float_int16(void *data) {
|
||||
// Returns a float value from an int16_t type
|
||||
return (mp_float_t)(*(int16_t *)data);
|
||||
}
|
||||
|
||||
|
||||
mp_float_t ndarray_get_float_float(void *data) {
|
||||
// Returns a float value from an mp_float_t type
|
||||
return *((mp_float_t *)data);
|
||||
}
|
||||
|
||||
// returns a single function pointer, depending on the dtype
|
||||
void *ndarray_get_float_function(uint8_t dtype) {
|
||||
if(dtype == NDARRAY_UINT8) {
|
||||
return ndarray_get_float_uint8;
|
||||
} else if(dtype == NDARRAY_INT8) {
|
||||
return ndarray_get_float_int8;
|
||||
} else if(dtype == NDARRAY_UINT16) {
|
||||
return ndarray_get_float_uint16;
|
||||
} else if(dtype == NDARRAY_INT16) {
|
||||
return ndarray_get_float_int16;
|
||||
} else {
|
||||
return ndarray_get_float_float;
|
||||
}
|
||||
}
|
||||
|
||||
mp_float_t ndarray_get_float_index(void *data, uint8_t dtype, size_t index) {
|
||||
// returns a single float value from an array located at index
|
||||
if(dtype == NDARRAY_UINT8) {
|
||||
return (mp_float_t)((uint8_t *)data)[index];
|
||||
} else if(dtype == NDARRAY_INT8) {
|
||||
return (mp_float_t)((int8_t *)data)[index];
|
||||
} else if(dtype == NDARRAY_UINT16) {
|
||||
return (mp_float_t)((uint16_t *)data)[index];
|
||||
} else if(dtype == NDARRAY_INT16) {
|
||||
return (mp_float_t)((int16_t *)data)[index];
|
||||
} else {
|
||||
return (mp_float_t)((mp_float_t *)data)[index];
|
||||
}
|
||||
}
|
||||
|
||||
mp_float_t ndarray_get_float_value(void *data, uint8_t dtype) {
|
||||
// Returns a float value from an arbitrary data type
|
||||
// The value in question is supposed to be located at the head of the pointer
|
||||
if(dtype == NDARRAY_UINT8) {
|
||||
return (mp_float_t)(*(uint8_t *)data);
|
||||
} else if(dtype == NDARRAY_INT8) {
|
||||
return (mp_float_t)(*(int8_t *)data);
|
||||
} else if(dtype == NDARRAY_UINT16) {
|
||||
return (mp_float_t)(*(uint16_t *)data);
|
||||
} else if(dtype == NDARRAY_INT16) {
|
||||
return (mp_float_t)(*(int16_t *)data);
|
||||
} else {
|
||||
return *((mp_float_t *)data);
|
||||
}
|
||||
}
|
||||
|
||||
#if NDARRAY_BINARY_USES_FUN_POINTER | ULAB_NUMPY_HAS_WHERE
|
||||
uint8_t ndarray_upcast_dtype(uint8_t ldtype, uint8_t rdtype) {
|
||||
// returns a single character that corresponds to the broadcasting rules
|
||||
// - if one of the operarands is a float, the result is always float
|
||||
// - operation on identical types preserves type
|
||||
//
|
||||
// uint8 + int8 => int16
|
||||
// uint8 + int16 => int16
|
||||
// uint8 + uint16 => uint16
|
||||
// int8 + int16 => int16
|
||||
// int8 + uint16 => uint16
|
||||
// uint16 + int16 => float
|
||||
|
||||
if(ldtype == rdtype) {
|
||||
// if the two dtypes are equal, the result is also of that type
|
||||
return ldtype;
|
||||
} else if(((ldtype == NDARRAY_UINT8) && (rdtype == NDARRAY_INT8)) ||
|
||||
((ldtype == NDARRAY_INT8) && (rdtype == NDARRAY_UINT8)) ||
|
||||
((ldtype == NDARRAY_UINT8) && (rdtype == NDARRAY_INT16)) ||
|
||||
((ldtype == NDARRAY_INT16) && (rdtype == NDARRAY_UINT8)) ||
|
||||
((ldtype == NDARRAY_INT8) && (rdtype == NDARRAY_INT16)) ||
|
||||
((ldtype == NDARRAY_INT16) && (rdtype == NDARRAY_INT8))) {
|
||||
return NDARRAY_INT16;
|
||||
} else if(((ldtype == NDARRAY_UINT8) && (rdtype == NDARRAY_UINT16)) ||
|
||||
((ldtype == NDARRAY_UINT16) && (rdtype == NDARRAY_UINT8)) ||
|
||||
((ldtype == NDARRAY_INT8) && (rdtype == NDARRAY_UINT16)) ||
|
||||
((ldtype == NDARRAY_UINT16) && (rdtype == NDARRAY_INT8))) {
|
||||
return NDARRAY_UINT16;
|
||||
}
|
||||
return NDARRAY_FLOAT;
|
||||
}
|
||||
|
||||
// The following five functions are the inverse of the ndarray_get_... functions,
|
||||
// and write a floating point datum into a void pointer
|
||||
|
||||
void ndarray_set_float_uint8(void *data, mp_float_t datum) {
|
||||
*((uint8_t *)data) = (uint8_t)datum;
|
||||
}
|
||||
|
||||
void ndarray_set_float_int8(void *data, mp_float_t datum) {
|
||||
*((int8_t *)data) = (int8_t)datum;
|
||||
}
|
||||
|
||||
void ndarray_set_float_uint16(void *data, mp_float_t datum) {
|
||||
*((uint16_t *)data) = (uint16_t)datum;
|
||||
}
|
||||
|
||||
void ndarray_set_float_int16(void *data, mp_float_t datum) {
|
||||
*((int16_t *)data) = (int16_t)datum;
|
||||
}
|
||||
|
||||
void ndarray_set_float_float(void *data, mp_float_t datum) {
|
||||
*((mp_float_t *)data) = datum;
|
||||
}
|
||||
|
||||
// returns a single function pointer, depending on the dtype
|
||||
void *ndarray_set_float_function(uint8_t dtype) {
|
||||
if(dtype == NDARRAY_UINT8) {
|
||||
return ndarray_set_float_uint8;
|
||||
} else if(dtype == NDARRAY_INT8) {
|
||||
return ndarray_set_float_int8;
|
||||
} else if(dtype == NDARRAY_UINT16) {
|
||||
return ndarray_set_float_uint16;
|
||||
} else if(dtype == NDARRAY_INT16) {
|
||||
return ndarray_set_float_int16;
|
||||
} else {
|
||||
return ndarray_set_float_float;
|
||||
}
|
||||
}
|
||||
#endif /* NDARRAY_BINARY_USES_FUN_POINTER */
|
||||
|
||||
int8_t tools_get_axis(mp_obj_t axis, uint8_t ndim) {
|
||||
int8_t ax = mp_obj_get_int(axis);
|
||||
if(ax < 0) ax += ndim;
|
||||
if((ax < 0) || (ax > ndim - 1)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("axis is out of bounds"));
|
||||
}
|
||||
return ax;
|
||||
}
|
||||
|
||||
shape_strides tools_reduce_axes(ndarray_obj_t *ndarray, mp_obj_t axis) {
|
||||
// TODO: replace numerical_reduce_axes with this function, wherever applicable
|
||||
// This function should be used, whenever a tensor is contracted;
|
||||
// The shape and strides at `axis` are moved to the zeroth position,
|
||||
// everything else is aligned to the right
|
||||
if(!mp_obj_is_int(axis) & (axis != mp_const_none)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("axis must be None, or an integer"));
|
||||
}
|
||||
shape_strides _shape_strides;
|
||||
|
||||
_shape_strides.increment = 0;
|
||||
// this is the contracted dimension (won't be overwritten for axis == None)
|
||||
_shape_strides.ndim = 0;
|
||||
|
||||
if(axis == mp_const_none) {
|
||||
_shape_strides.shape = ndarray->shape;
|
||||
_shape_strides.strides = ndarray->strides;
|
||||
return _shape_strides;
|
||||
}
|
||||
|
||||
size_t *shape = m_new(size_t, ULAB_MAX_DIMS + 1);
|
||||
_shape_strides.shape = shape;
|
||||
int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS + 1);
|
||||
_shape_strides.strides = strides;
|
||||
|
||||
memcpy(_shape_strides.shape, ndarray->shape, sizeof(size_t) * ULAB_MAX_DIMS);
|
||||
memcpy(_shape_strides.strides, ndarray->strides, sizeof(int32_t) * ULAB_MAX_DIMS);
|
||||
|
||||
_shape_strides.axis = ULAB_MAX_DIMS - 1; // value of index for axis == mp_const_none (won't be overwritten)
|
||||
|
||||
if(axis != mp_const_none) { // i.e., axis is an integer
|
||||
int8_t ax = tools_get_axis(axis, ndarray->ndim);
|
||||
_shape_strides.axis = ULAB_MAX_DIMS - ndarray->ndim + ax;
|
||||
_shape_strides.ndim = ndarray->ndim - 1;
|
||||
}
|
||||
|
||||
// move the value stored at index to the leftmost position, and align everything else to the right
|
||||
_shape_strides.shape[0] = ndarray->shape[_shape_strides.axis];
|
||||
_shape_strides.strides[0] = ndarray->strides[_shape_strides.axis];
|
||||
for(uint8_t i = 0; i < _shape_strides.axis; i++) {
|
||||
// entries to the right of index must be shifted by one position to the left
|
||||
_shape_strides.shape[i + 1] = ndarray->shape[i];
|
||||
_shape_strides.strides[i + 1] = ndarray->strides[i];
|
||||
}
|
||||
|
||||
if(_shape_strides.ndim != 0) {
|
||||
_shape_strides.increment = 1;
|
||||
}
|
||||
|
||||
if(_shape_strides.ndim == 0) {
|
||||
_shape_strides.ndim = 1;
|
||||
_shape_strides.shape[ULAB_MAX_DIMS - 1] = 1;
|
||||
_shape_strides.strides[ULAB_MAX_DIMS - 1] = ndarray->itemsize;
|
||||
}
|
||||
|
||||
return _shape_strides;
|
||||
}
|
||||
|
||||
mp_obj_t ulab_tools_restore_dims(ndarray_obj_t *ndarray, ndarray_obj_t *results, mp_obj_t keepdims, shape_strides _shape_strides) {
|
||||
// restores the contracted dimension, if keepdims is True
|
||||
if((ndarray->ndim == 1) && (keepdims != mp_const_true)) {
|
||||
// since the original array has already been contracted and
|
||||
// we don't want to keep the dimensions here, we have to return a scalar
|
||||
return mp_binary_get_val_array(results->dtype, results->array, 0);
|
||||
}
|
||||
|
||||
if(keepdims == mp_const_true) {
|
||||
results->ndim += 1;
|
||||
for(int8_t i = 0; i < ULAB_MAX_DIMS; i++) {
|
||||
results->shape[i] = ndarray->shape[i];
|
||||
}
|
||||
results->shape[_shape_strides.axis] = 1;
|
||||
|
||||
results->strides[ULAB_MAX_DIMS - 1] = ndarray->itemsize;
|
||||
for(uint8_t i = ULAB_MAX_DIMS; i > 1; i--) {
|
||||
results->strides[i - 2] = results->strides[i - 1] * results->shape[i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
return MP_OBJ_FROM_PTR(results);
|
||||
}
|
||||
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
ndarray_obj_t *tools_object_is_square(mp_obj_t obj) {
|
||||
// Returns an ndarray, if the object is a square ndarray,
|
||||
// raises the appropriate exception otherwise
|
||||
if(!mp_obj_is_type(obj, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("size is defined for ndarrays only"));
|
||||
}
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(obj);
|
||||
if((ndarray->shape[ULAB_MAX_DIMS - 1] != ndarray->shape[ULAB_MAX_DIMS - 2]) || (ndarray->ndim != 2)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("input must be square matrix"));
|
||||
}
|
||||
return ndarray;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint8_t ulab_binary_get_size(uint8_t dtype) {
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
if(dtype == NDARRAY_COMPLEX) {
|
||||
return 2 * (uint8_t)sizeof(mp_float_t);
|
||||
}
|
||||
#endif
|
||||
return dtype == NDARRAY_BOOL ? 1 : mp_binary_get_size('@', dtype, NULL);
|
||||
}
|
||||
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
void ulab_rescale_float_strides(int32_t *strides) {
|
||||
// re-scale the strides, so that we can work with floats, when iterating
|
||||
uint8_t sz = sizeof(mp_float_t);
|
||||
for(uint8_t i = 0; i < ULAB_MAX_DIMS; i++) {
|
||||
strides[i] /= sz;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool ulab_tools_mp_obj_is_scalar(mp_obj_t obj) {
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
if(mp_obj_is_int(obj) || mp_obj_is_float(obj) || mp_obj_is_type(obj, &mp_type_complex)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if(mp_obj_is_int(obj) || mp_obj_is_float(obj)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ndarray_obj_t *ulab_tools_inspect_out(mp_obj_t out, uint8_t dtype, uint8_t ndim, size_t *shape, bool dense_only) {
|
||||
if(!mp_obj_is_type(out, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("out has wrong type"));
|
||||
}
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(out);
|
||||
|
||||
if(ndarray->dtype != dtype) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("out array has wrong dtype"));
|
||||
}
|
||||
|
||||
if(ndarray->ndim != ndim) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("out array has wrong dimension"));
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < ULAB_MAX_DIMS; i++) {
|
||||
if(ndarray->shape[i] != shape[i]) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("out array has wrong shape"));
|
||||
}
|
||||
}
|
||||
|
||||
if(dense_only) {
|
||||
if(!ndarray_is_dense(ndarray)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("output array must be contiguous"));
|
||||
}
|
||||
}
|
||||
return ndarray;
|
||||
}
|
||||
51
code/ulab_tools.h
Normal file
51
code/ulab_tools.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2022 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _TOOLS_
|
||||
#define _TOOLS_
|
||||
|
||||
#include "ndarray.h"
|
||||
|
||||
#define SWAP(t, a, b) { t tmp = a; a = b; b = tmp; }
|
||||
|
||||
typedef struct _shape_strides_t {
|
||||
uint8_t increment;
|
||||
uint8_t axis;
|
||||
uint8_t ndim;
|
||||
size_t *shape;
|
||||
int32_t *strides;
|
||||
} shape_strides;
|
||||
|
||||
mp_float_t ndarray_get_float_uint8(void *);
|
||||
mp_float_t ndarray_get_float_int8(void *);
|
||||
mp_float_t ndarray_get_float_uint16(void *);
|
||||
mp_float_t ndarray_get_float_int16(void *);
|
||||
mp_float_t ndarray_get_float_float(void *);
|
||||
void *ndarray_get_float_function(uint8_t );
|
||||
|
||||
uint8_t ndarray_upcast_dtype(uint8_t , uint8_t );
|
||||
void *ndarray_set_float_function(uint8_t );
|
||||
|
||||
shape_strides tools_reduce_axes(ndarray_obj_t *, mp_obj_t );
|
||||
int8_t tools_get_axis(mp_obj_t , uint8_t );
|
||||
mp_obj_t ulab_tools_restore_dims(ndarray_obj_t * , ndarray_obj_t * , mp_obj_t , shape_strides );
|
||||
ndarray_obj_t *tools_object_is_square(mp_obj_t );
|
||||
|
||||
uint8_t ulab_binary_get_size(uint8_t );
|
||||
|
||||
#if ULAB_SUPPORTS_COMPLEX
|
||||
void ulab_rescale_float_strides(int32_t *);
|
||||
#endif
|
||||
|
||||
bool ulab_tools_mp_obj_is_scalar(mp_obj_t );
|
||||
|
||||
ndarray_obj_t *ulab_tools_inspect_out(mp_obj_t , uint8_t , uint8_t , size_t *, bool );
|
||||
|
||||
#endif
|
||||
98
code/user/user.c
Normal file
98
code/user/user.c
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2021 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
#include "user.h"
|
||||
|
||||
#if ULAB_HAS_USER_MODULE
|
||||
|
||||
//| """This module should hold arbitrary user-defined functions."""
|
||||
//|
|
||||
|
||||
static mp_obj_t user_square(mp_obj_t arg) {
|
||||
// the function takes a single dense ndarray, and calculates the
|
||||
// element-wise square of its entries
|
||||
|
||||
// raise a TypeError exception, if the input is not an ndarray
|
||||
if(!mp_obj_is_type(arg, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("input must be an ndarray"));
|
||||
}
|
||||
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(arg);
|
||||
|
||||
// make sure that the input is a dense array
|
||||
if(!ndarray_is_dense(ndarray)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("input must be a dense ndarray"));
|
||||
}
|
||||
|
||||
// if the input is a dense array, create `results` with the same number of
|
||||
// dimensions, shape, and dtype
|
||||
ndarray_obj_t *results = ndarray_new_dense_ndarray(ndarray->ndim, ndarray->shape, ndarray->dtype);
|
||||
|
||||
// since in a dense array the iteration over the elements is trivial, we
|
||||
// can cast the data arrays ndarray->array and results->array to the actual type
|
||||
if(ndarray->dtype == NDARRAY_UINT8) {
|
||||
uint8_t *array = (uint8_t *)ndarray->array;
|
||||
uint8_t *rarray = (uint8_t *)results->array;
|
||||
for(size_t i=0; i < ndarray->len; i++, array++) {
|
||||
*rarray++ = (*array) * (*array);
|
||||
}
|
||||
} else if(ndarray->dtype == NDARRAY_INT8) {
|
||||
int8_t *array = (int8_t *)ndarray->array;
|
||||
int8_t *rarray = (int8_t *)results->array;
|
||||
for(size_t i=0; i < ndarray->len; i++, array++) {
|
||||
*rarray++ = (*array) * (*array);
|
||||
}
|
||||
} else if(ndarray->dtype == NDARRAY_UINT16) {
|
||||
uint16_t *array = (uint16_t *)ndarray->array;
|
||||
uint16_t *rarray = (uint16_t *)results->array;
|
||||
for(size_t i=0; i < ndarray->len; i++, array++) {
|
||||
*rarray++ = (*array) * (*array);
|
||||
}
|
||||
} else if(ndarray->dtype == NDARRAY_INT16) {
|
||||
int16_t *array = (int16_t *)ndarray->array;
|
||||
int16_t *rarray = (int16_t *)results->array;
|
||||
for(size_t i=0; i < ndarray->len; i++, array++) {
|
||||
*rarray++ = (*array) * (*array);
|
||||
}
|
||||
} else { // if we end up here, the dtype is NDARRAY_FLOAT
|
||||
mp_float_t *array = (mp_float_t *)ndarray->array;
|
||||
mp_float_t *rarray = (mp_float_t *)results->array;
|
||||
for(size_t i=0; i < ndarray->len; i++, array++) {
|
||||
*rarray++ = (*array) * (*array);
|
||||
}
|
||||
}
|
||||
// at the end, return a micrppython object
|
||||
return MP_OBJ_FROM_PTR(results);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(user_square_obj, user_square);
|
||||
|
||||
static const mp_rom_map_elem_t ulab_user_globals_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_user) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_square), (mp_obj_t)&user_square_obj },
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT(mp_module_ulab_user_globals, ulab_user_globals_table);
|
||||
|
||||
const mp_obj_module_t ulab_user_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_user_globals,
|
||||
};
|
||||
#if CIRCUITPY_ULAB
|
||||
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_user, ulab_user_module);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
20
code/user/user.h
Normal file
20
code/user/user.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2021 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _USER_
|
||||
#define _USER_
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "../ndarray.h"
|
||||
|
||||
extern const mp_obj_module_t ulab_user_module;
|
||||
|
||||
#endif
|
||||
414
code/utils/utils.c
Normal file
414
code/utils/utils.c
Normal file
|
|
@ -0,0 +1,414 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2024 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "../ulab_tools.h"
|
||||
#include "../numpy/fft/fft_tools.h"
|
||||
|
||||
#if ULAB_HAS_UTILS_MODULE
|
||||
|
||||
enum UTILS_BUFFER_TYPE {
|
||||
UTILS_INT16_BUFFER,
|
||||
UTILS_UINT16_BUFFER,
|
||||
UTILS_INT32_BUFFER,
|
||||
UTILS_UINT32_BUFFER,
|
||||
};
|
||||
|
||||
#if ULAB_UTILS_HAS_FROM_INT16_BUFFER | ULAB_UTILS_HAS_FROM_UINT16_BUFFER | ULAB_UTILS_HAS_FROM_INT32_BUFFER | ULAB_UTILS_HAS_FROM_UINT32_BUFFER
|
||||
static mp_obj_t utils_from_intbuffer_helper(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, uint8_t buffer_type) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } } ,
|
||||
{ MP_QSTR_count, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_INT(-1) } },
|
||||
{ MP_QSTR_offset, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_INT(0) } },
|
||||
{ MP_QSTR_out, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_byteswap, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_FALSE } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
ndarray_obj_t *ndarray = NULL;
|
||||
|
||||
if(args[3].u_obj != mp_const_none) {
|
||||
ndarray = MP_OBJ_TO_PTR(args[3].u_obj);
|
||||
if((ndarray->dtype != NDARRAY_FLOAT) || !ndarray_is_dense(ndarray)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("out must be a float dense array"));
|
||||
}
|
||||
}
|
||||
|
||||
size_t offset = mp_obj_get_int(args[2].u_obj);
|
||||
|
||||
mp_buffer_info_t bufinfo;
|
||||
if(mp_get_buffer(args[0].u_obj, &bufinfo, MP_BUFFER_READ)) {
|
||||
if(bufinfo.len < offset) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("offset is too large"));
|
||||
}
|
||||
uint8_t sz = sizeof(int16_t);
|
||||
#if ULAB_UTILS_HAS_FROM_INT32_BUFFER | ULAB_UTILS_HAS_FROM_UINT32_BUFFER
|
||||
if((buffer_type == UTILS_INT32_BUFFER) || (buffer_type == UTILS_UINT32_BUFFER)) {
|
||||
sz = sizeof(int32_t);
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t len = (bufinfo.len - offset) / sz;
|
||||
if((len * sz) != (bufinfo.len - offset)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("buffer size must be a multiple of element size"));
|
||||
}
|
||||
if(mp_obj_get_int(args[1].u_obj) > 0) {
|
||||
size_t count = mp_obj_get_int(args[1].u_obj);
|
||||
if(len < count) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("buffer is smaller than requested size"));
|
||||
} else {
|
||||
len = count;
|
||||
}
|
||||
}
|
||||
if(args[3].u_obj == mp_const_none) {
|
||||
ndarray = ndarray_new_linear_array(len, NDARRAY_FLOAT);
|
||||
} else {
|
||||
if(ndarray->len < len) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("out array is too small"));
|
||||
}
|
||||
}
|
||||
uint8_t *buffer = bufinfo.buf;
|
||||
|
||||
mp_float_t *array = (mp_float_t *)ndarray->array;
|
||||
if(args[4].u_obj == mp_const_true) {
|
||||
// swap the bytes before conversion
|
||||
uint8_t *tmpbuff = m_new(uint8_t, sz);
|
||||
#if ULAB_UTILS_HAS_FROM_INT16_BUFFER | ULAB_UTILS_HAS_FROM_UINT16_BUFFER
|
||||
if((buffer_type == UTILS_INT16_BUFFER) || (buffer_type == UTILS_UINT16_BUFFER)) {
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
tmpbuff += sz;
|
||||
for(uint8_t j = 0; j < sz; j++) {
|
||||
memcpy(--tmpbuff, buffer++, 1);
|
||||
}
|
||||
if(buffer_type == UTILS_INT16_BUFFER) {
|
||||
*array++ = (mp_float_t)(*(int16_t *)tmpbuff);
|
||||
} else {
|
||||
*array++ = (mp_float_t)(*(uint16_t *)tmpbuff);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if ULAB_UTILS_HAS_FROM_INT32_BUFFER | ULAB_UTILS_HAS_FROM_UINT32_BUFFER
|
||||
if((buffer_type == UTILS_INT32_BUFFER) || (buffer_type == UTILS_UINT32_BUFFER)) {
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
tmpbuff += sz;
|
||||
for(uint8_t j = 0; j < sz; j++) {
|
||||
memcpy(--tmpbuff, buffer++, 1);
|
||||
}
|
||||
if(buffer_type == UTILS_INT32_BUFFER) {
|
||||
*array++ = (mp_float_t)(*(int32_t *)tmpbuff);
|
||||
} else {
|
||||
*array++ = (mp_float_t)(*(uint32_t *)tmpbuff);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
#if ULAB_UTILS_HAS_FROM_INT16_BUFFER
|
||||
if(buffer_type == UTILS_INT16_BUFFER) {
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
*array++ = (mp_float_t)(*(int16_t *)buffer);
|
||||
buffer += sz;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if ULAB_UTILS_HAS_FROM_UINT16_BUFFER
|
||||
if(buffer_type == UTILS_UINT16_BUFFER) {
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
*array++ = (mp_float_t)(*(uint16_t *)buffer);
|
||||
buffer += sz;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if ULAB_UTILS_HAS_FROM_INT32_BUFFER
|
||||
if(buffer_type == UTILS_INT32_BUFFER) {
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
*array++ = (mp_float_t)(*(int32_t *)buffer);
|
||||
buffer += sz;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if ULAB_UTILS_HAS_FROM_UINT32_BUFFER
|
||||
if(buffer_type == UTILS_UINT32_BUFFER) {
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
*array++ = (mp_float_t)(*(uint32_t *)buffer);
|
||||
buffer += sz;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
#ifdef ULAB_UTILS_HAS_FROM_INT16_BUFFER
|
||||
static mp_obj_t utils_from_int16_buffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return utils_from_intbuffer_helper(n_args, pos_args, kw_args, UTILS_INT16_BUFFER);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(utils_from_int16_buffer_obj, 1, utils_from_int16_buffer);
|
||||
#endif
|
||||
|
||||
#ifdef ULAB_UTILS_HAS_FROM_UINT16_BUFFER
|
||||
static mp_obj_t utils_from_uint16_buffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return utils_from_intbuffer_helper(n_args, pos_args, kw_args, UTILS_UINT16_BUFFER);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(utils_from_uint16_buffer_obj, 1, utils_from_uint16_buffer);
|
||||
#endif
|
||||
|
||||
#ifdef ULAB_UTILS_HAS_FROM_INT32_BUFFER
|
||||
static mp_obj_t utils_from_int32_buffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return utils_from_intbuffer_helper(n_args, pos_args, kw_args, UTILS_INT32_BUFFER);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(utils_from_int32_buffer_obj, 1, utils_from_int32_buffer);
|
||||
#endif
|
||||
|
||||
#ifdef ULAB_UTILS_HAS_FROM_UINT32_BUFFER
|
||||
static mp_obj_t utils_from_uint32_buffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
return utils_from_intbuffer_helper(n_args, pos_args, kw_args, UTILS_UINT32_BUFFER);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(utils_from_uint32_buffer_obj, 1, utils_from_uint32_buffer);
|
||||
#endif
|
||||
|
||||
#endif /* ULAB_UTILS_HAS_FROM_INT16_BUFFER | ULAB_UTILS_HAS_FROM_UINT16_BUFFER | ULAB_UTILS_HAS_FROM_INT32_BUFFER | ULAB_UTILS_HAS_FROM_UINT32_BUFFER */
|
||||
|
||||
#if ULAB_UTILS_HAS_SPECTROGRAM
|
||||
//| import ulab.numpy
|
||||
//|
|
||||
//| def spectrogram(r: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
|
||||
//| """
|
||||
//| :param ulab.numpy.ndarray r: A 1-dimension array of values whose size is a power of 2
|
||||
//|
|
||||
//| Computes the spectrum of the input signal. This is the absolute value of the (complex-valued) fft of the signal.
|
||||
//| This function is similar to scipy's ``scipy.signal.welch`` https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.welch.html."""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
mp_obj_t utils_spectrogram(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE }} ,
|
||||
#if !ULAB_FFT_IS_NUMPY_COMPATIBLE
|
||||
{ MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
#endif
|
||||
{ MP_QSTR_scratchpad, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_log, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_FALSE } },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
|
||||
mp_raise_NotImplementedError(MP_ERROR_TEXT("spectrogram is defined for ndarrays only"));
|
||||
}
|
||||
ndarray_obj_t *in = MP_OBJ_TO_PTR(args[0].u_obj);
|
||||
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
if(in->ndim != 1) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("spectrogram is implemented for 1D arrays only"));
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t len = in->len;
|
||||
// Check if input is of length of power of 2
|
||||
if((len & (len-1)) != 0) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("input array length must be power of 2"));
|
||||
}
|
||||
|
||||
ndarray_obj_t *out = NULL;
|
||||
|
||||
#if ULAB_FFT_IS_NUMPY_COMPATIBLE
|
||||
mp_obj_t scratchpad_object = args[1].u_obj;
|
||||
mp_obj_t out_object = args[2].u_obj;
|
||||
mp_obj_t log_object = args[3].u_obj;
|
||||
#else
|
||||
mp_obj_t scratchpad_object = args[2].u_obj;
|
||||
mp_obj_t out_object = args[3].u_obj;
|
||||
mp_obj_t log_object = args[4].u_obj;
|
||||
#endif
|
||||
|
||||
if(out_object != mp_const_none) {
|
||||
if(!mp_obj_is_type(out_object, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("out must be an ndarray"));
|
||||
}
|
||||
|
||||
out = MP_OBJ_TO_PTR(out_object);
|
||||
if((out->dtype != NDARRAY_FLOAT) || (out->ndim != 1)){
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("out array must be a 1D array of float type"));
|
||||
}
|
||||
if(len != out->len) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("input and out arrays must have same length"));
|
||||
}
|
||||
} else {
|
||||
out = ndarray_new_linear_array(len, NDARRAY_FLOAT);
|
||||
}
|
||||
|
||||
ndarray_obj_t *scratchpad = NULL;
|
||||
mp_float_t *tmp = NULL;
|
||||
|
||||
if(scratchpad_object != mp_const_none) {
|
||||
if(!mp_obj_is_type(scratchpad_object, &ulab_ndarray_type)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("scratchpad must be an ndarray"));
|
||||
}
|
||||
|
||||
scratchpad = MP_OBJ_TO_PTR(scratchpad_object);
|
||||
if(!ndarray_is_dense(scratchpad) || (scratchpad->ndim != 1) || (scratchpad->dtype != NDARRAY_FLOAT)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("scratchpad must be a 1D dense float array"));
|
||||
}
|
||||
if(scratchpad->len != 2 * len) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("scratchpad must be twice as long as input"));
|
||||
}
|
||||
|
||||
tmp = (mp_float_t *)scratchpad->array;
|
||||
} else {
|
||||
tmp = m_new0(mp_float_t, 2 * len);
|
||||
}
|
||||
|
||||
uint8_t *array = (uint8_t *)in->array;
|
||||
|
||||
#if ULAB_FFT_IS_NUMPY_COMPATIBLE & ULAB_SUPPORTS_COMPLEX
|
||||
if(in->dtype == NDARRAY_COMPLEX) {
|
||||
uint8_t sz = 2 * sizeof(mp_float_t);
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
memcpy(tmp, array, sz);
|
||||
tmp += 2;
|
||||
array += in->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
} else {
|
||||
mp_float_t (*func)(void *) = ndarray_get_float_function(in->dtype);
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
*tmp++ = func(array); // real part
|
||||
*tmp++ = 0; // imaginary part, clear
|
||||
array += in->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
}
|
||||
|
||||
tmp -= 2 * len;
|
||||
fft_kernel(tmp, len, 1);
|
||||
#else // we might have two real input vectors
|
||||
|
||||
ndarray_obj_t *in2 = NULL;
|
||||
|
||||
if(n_args == 2) {
|
||||
if(!mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) {
|
||||
mp_raise_NotImplementedError(MP_ERROR_TEXT("spectrogram is defined for ndarrays only"));
|
||||
}
|
||||
in2 = MP_OBJ_TO_PTR(args[1].u_obj);
|
||||
|
||||
#if ULAB_MAX_DIMS > 1
|
||||
if(in2->ndim != 1) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("spectrogram is implemented for 1D arrays only"));
|
||||
}
|
||||
#endif
|
||||
if(len != in2->len) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("input arrays are not compatible"));
|
||||
}
|
||||
}
|
||||
|
||||
mp_float_t (*func)(void *) = ndarray_get_float_function(in->dtype);
|
||||
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
*tmp++ = func(array); // real part; imageinary will be cleared later
|
||||
array += in->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
|
||||
if(n_args == 2) {
|
||||
mp_float_t (*func2)(void *) = ndarray_get_float_function(in2->dtype);
|
||||
array = (uint8_t *)in2->array;
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
*tmp++ = func2(array);
|
||||
array += in2->strides[ULAB_MAX_DIMS - 1];
|
||||
}
|
||||
tmp -= len;
|
||||
} else {
|
||||
// if there is only one input argument, clear the imaginary part
|
||||
memset(tmp, 0, len * sizeof(mp_float_t));
|
||||
}
|
||||
|
||||
tmp -= len;
|
||||
|
||||
fft_kernel(tmp, tmp + len, len, 1);
|
||||
#endif /* ULAB_FFT_IS_NUMPY_COMPATIBLE */
|
||||
|
||||
mp_float_t *spectrum = (mp_float_t *)out->array;
|
||||
uint8_t spectrum_sz = out->strides[ULAB_MAX_DIMS - 1] / sizeof(mp_float_t);
|
||||
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
#if ULAB_FFT_IS_NUMPY_COMPATIBLE
|
||||
*spectrum = MICROPY_FLOAT_C_FUN(sqrt)(*tmp * *tmp + *(tmp + 1) * *(tmp + 1));
|
||||
tmp += 2;
|
||||
#else
|
||||
*spectrum = MICROPY_FLOAT_C_FUN(sqrt)(*tmp * *tmp + *(tmp + len) * *(tmp + len));
|
||||
tmp++;
|
||||
#endif
|
||||
if(log_object == mp_const_true) {
|
||||
*spectrum = MICROPY_FLOAT_C_FUN(log)(*spectrum);
|
||||
}
|
||||
spectrum += spectrum_sz;
|
||||
}
|
||||
|
||||
if(scratchpad_object == mp_const_none) {
|
||||
tmp -= len;
|
||||
#if ULAB_FFT_IS_NUMPY_COMPATIBLE
|
||||
tmp -= len;
|
||||
#endif
|
||||
m_del(mp_float_t, tmp, 2 * len);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(out);
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(utils_spectrogram_obj, 1, utils_spectrogram);
|
||||
|
||||
#endif /* ULAB_UTILS_HAS_SPECTROGRAM */
|
||||
|
||||
|
||||
static const mp_rom_map_elem_t ulab_utils_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_utils) },
|
||||
#if ULAB_UTILS_HAS_FROM_INT16_BUFFER
|
||||
{ MP_ROM_QSTR(MP_QSTR_from_int16_buffer), MP_ROM_PTR(&utils_from_int16_buffer_obj) },
|
||||
#endif
|
||||
#if ULAB_UTILS_HAS_FROM_UINT16_BUFFER
|
||||
{ MP_ROM_QSTR(MP_QSTR_from_uint16_buffer), MP_ROM_PTR(&utils_from_uint16_buffer_obj) },
|
||||
#endif
|
||||
#if ULAB_UTILS_HAS_FROM_INT32_BUFFER
|
||||
{ MP_ROM_QSTR(MP_QSTR_from_int32_buffer), MP_ROM_PTR(&utils_from_int32_buffer_obj) },
|
||||
#endif
|
||||
#if ULAB_UTILS_HAS_FROM_UINT32_BUFFER
|
||||
{ MP_ROM_QSTR(MP_QSTR_from_uint32_buffer), MP_ROM_PTR(&utils_from_uint32_buffer_obj) },
|
||||
#endif
|
||||
#if ULAB_UTILS_HAS_SPECTROGRAM
|
||||
{ MP_ROM_QSTR(MP_QSTR_spectrogram), MP_ROM_PTR(&utils_spectrogram_obj) },
|
||||
#endif
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT(mp_module_ulab_utils_globals, ulab_utils_globals_table);
|
||||
|
||||
const mp_obj_module_t ulab_utils_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_ulab_utils_globals,
|
||||
};
|
||||
#if CIRCUITPY_ULAB
|
||||
MP_REGISTER_MODULE(MP_QSTR_ulab_dot_utils, ulab_utils_module);
|
||||
#endif
|
||||
|
||||
#endif /* ULAB_HAS_UTILS_MODULE */
|
||||
19
code/utils/utils.h
Normal file
19
code/utils/utils.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020-2021 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _UTILS_
|
||||
#define _UTILS_
|
||||
|
||||
#include "../ulab.h"
|
||||
#include "../ndarray.h"
|
||||
|
||||
extern const mp_obj_module_t ulab_utils_module;
|
||||
|
||||
#endif
|
||||
106
code/vectorise.c
106
code/vectorise.c
|
|
@ -1,106 +0,0 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "py/runtime.h"
|
||||
#include "py/binary.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/objarray.h"
|
||||
#include "vectorise.h"
|
||||
|
||||
#ifndef MP_PI
|
||||
#define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846)
|
||||
#endif
|
||||
|
||||
mp_obj_t vectorise_generic_vector(mp_obj_t o_in, mp_float_t (*f)(mp_float_t)) {
|
||||
// Return a single value, if o_in is not iterable
|
||||
if(mp_obj_is_float(o_in) || mp_obj_is_integer(o_in)) {
|
||||
return mp_obj_new_float(f(mp_obj_get_float(o_in)));
|
||||
}
|
||||
mp_float_t x;
|
||||
if(MP_OBJ_IS_TYPE(o_in, &ulab_ndarray_type)) {
|
||||
ndarray_obj_t *source = MP_OBJ_TO_PTR(o_in);
|
||||
ndarray_obj_t *ndarray = create_new_ndarray(source->m, source->n, NDARRAY_FLOAT);
|
||||
mp_float_t *dataout = (mp_float_t *)ndarray->array->items;
|
||||
if(source->array->typecode == NDARRAY_UINT8) {
|
||||
ITERATE_VECTOR(uint8_t, source, dataout);
|
||||
} else if(source->array->typecode == NDARRAY_INT8) {
|
||||
ITERATE_VECTOR(int8_t, source, dataout);
|
||||
} else if(source->array->typecode == NDARRAY_UINT16) {
|
||||
ITERATE_VECTOR(uint16_t, source, dataout);
|
||||
} else if(source->array->typecode == NDARRAY_INT16) {
|
||||
ITERATE_VECTOR(int16_t, source, dataout);
|
||||
} else {
|
||||
ITERATE_VECTOR(mp_float_t, source, dataout);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
} else if(MP_OBJ_IS_TYPE(o_in, &mp_type_tuple) || MP_OBJ_IS_TYPE(o_in, &mp_type_list) ||
|
||||
MP_OBJ_IS_TYPE(o_in, &mp_type_range)) { // i.e., the input is a generic iterable
|
||||
mp_obj_array_t *o = MP_OBJ_TO_PTR(o_in);
|
||||
ndarray_obj_t *out = create_new_ndarray(1, o->len, NDARRAY_FLOAT);
|
||||
mp_float_t *dataout = (mp_float_t *)out->array->items;
|
||||
mp_obj_iter_buf_t iter_buf;
|
||||
mp_obj_t item, iterable = mp_getiter(o_in, &iter_buf);
|
||||
size_t i=0;
|
||||
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
x = mp_obj_get_float(item);
|
||||
dataout[i++] = f(x);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(out);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
// _degrees won't compile for the unix port
|
||||
/*
|
||||
mp_float_t _degreesf(mp_float_t x) {
|
||||
return(180*x/MP_PI);
|
||||
}
|
||||
|
||||
MATH_FUN_1(degrees, _degrees);
|
||||
|
||||
// _radians won't compile for the unix port
|
||||
mp_float_t _radiansf(mp_float_t x) {
|
||||
return(MP_PI*x/180.0);
|
||||
}
|
||||
|
||||
MATH_FUN_1(radians, _radians);
|
||||
|
||||
STATIC mp_float_t _fabsf(mp_float_t x) {
|
||||
return fabsf(x);
|
||||
}
|
||||
|
||||
MATH_FUN_1(fabs, _fabs);
|
||||
*/
|
||||
MATH_FUN_1(acos, acos);
|
||||
MATH_FUN_1(acosh, acosh);
|
||||
MATH_FUN_1(asin, asin);
|
||||
MATH_FUN_1(asinh, asinh);
|
||||
MATH_FUN_1(atan, atan);
|
||||
MATH_FUN_1(atanh, atanh);
|
||||
MATH_FUN_1(ceil, ceil);
|
||||
MATH_FUN_1(cos, cos);
|
||||
MATH_FUN_1(erf, erf);
|
||||
MATH_FUN_1(erfc, erfc);
|
||||
MATH_FUN_1(exp, exp);
|
||||
MATH_FUN_1(expm1, expm1);
|
||||
MATH_FUN_1(floor, floor);
|
||||
MATH_FUN_1(gamma, tgamma);
|
||||
MATH_FUN_1(lgamma, lgamma);
|
||||
MATH_FUN_1(log, log);
|
||||
MATH_FUN_1(log10, log10);
|
||||
MATH_FUN_1(log2, log2);
|
||||
MATH_FUN_1(sin, sin);
|
||||
MATH_FUN_1(sinh, sinh);
|
||||
MATH_FUN_1(sqrt, sqrt);
|
||||
MATH_FUN_1(tan, tan);
|
||||
MATH_FUN_1(tanh, tanh);
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
* This file is part of the micropython-ulab project,
|
||||
*
|
||||
* https://github.com/v923z/micropython-ulab
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _VECTORISE_
|
||||
#define _VECTORISE_
|
||||
|
||||
#include "ndarray.h"
|
||||
|
||||
mp_obj_t vectorise_acos(mp_obj_t );
|
||||
mp_obj_t vectorise_acosh(mp_obj_t );
|
||||
mp_obj_t vectorise_asin(mp_obj_t );
|
||||
mp_obj_t vectorise_asinh(mp_obj_t );
|
||||
mp_obj_t vectorise_atan(mp_obj_t );
|
||||
mp_obj_t vectorise_atanh(mp_obj_t );
|
||||
mp_obj_t vectorise_ceil(mp_obj_t );
|
||||
mp_obj_t vectorise_cos(mp_obj_t );
|
||||
mp_obj_t vectorise_erf(mp_obj_t );
|
||||
mp_obj_t vectorise_erfc(mp_obj_t );
|
||||
mp_obj_t vectorise_exp(mp_obj_t );
|
||||
mp_obj_t vectorise_expm1(mp_obj_t );
|
||||
mp_obj_t vectorise_floor(mp_obj_t );
|
||||
mp_obj_t vectorise_gamma(mp_obj_t );
|
||||
mp_obj_t vectorise_lgamma(mp_obj_t );
|
||||
mp_obj_t vectorise_log(mp_obj_t );
|
||||
mp_obj_t vectorise_log10(mp_obj_t );
|
||||
mp_obj_t vectorise_log2(mp_obj_t );
|
||||
mp_obj_t vectorise_sin(mp_obj_t );
|
||||
mp_obj_t vectorise_sinh(mp_obj_t );
|
||||
mp_obj_t vectorise_sqrt(mp_obj_t );
|
||||
mp_obj_t vectorise_tan(mp_obj_t );
|
||||
mp_obj_t vectorise_tanh(mp_obj_t );
|
||||
|
||||
#define ITERATE_VECTOR(type, souce, out) do {\
|
||||
type *input = (type *)(source)->array->items;\
|
||||
for(size_t i=0; i < (source)->array->len; i++) {\
|
||||
(out)[i] = f(input[i]);\
|
||||
}\
|
||||
} while(0)
|
||||
|
||||
#define MATH_FUN_1(py_name, c_name) \
|
||||
mp_obj_t vectorise_ ## py_name(mp_obj_t x_obj) { \
|
||||
return vectorise_generic_vector(x_obj, MICROPY_FLOAT_C_FUN(c_name)); \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -14,6 +14,10 @@ help:
|
|||
|
||||
.PHONY: help Makefile
|
||||
|
||||
clean:
|
||||
rm -rf "$(BUILDDIR)"
|
||||
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
|
|
|
|||
35
docs/manual/make.bat
Normal file
35
docs/manual/make.bat
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=source
|
||||
set BUILDDIR=build
|
||||
|
||||
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% %O%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
|
||||
:end
|
||||
popd
|
||||
|
|
@ -10,19 +10,24 @@
|
|||
# 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 os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
#import sphinx_rtd_theme
|
||||
|
||||
from sphinx.transforms import SphinxTransform
|
||||
from docutils import nodes
|
||||
from sphinx import addnodes
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'micropython-ulab'
|
||||
copyright = '2019, Zoltán Vörös'
|
||||
project = 'The ulab book'
|
||||
copyright = '2019-2025, Zoltán Vörös and contributors'
|
||||
author = 'Zoltán Vörös'
|
||||
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = '0.24'
|
||||
release = '6.9.0'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
|
@ -42,18 +47,46 @@ templates_path = ['_templates']
|
|||
exclude_patterns = []
|
||||
|
||||
|
||||
# -- 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'
|
||||
|
||||
# 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']
|
||||
|
||||
latex_maketitle = r'''
|
||||
\begin{titlepage}
|
||||
\begin{flushright}
|
||||
\Huge\textbf{The $\mu$lab book}
|
||||
\vskip 0.5em
|
||||
\LARGE
|
||||
\textbf{Release %s}
|
||||
\vskip 5em
|
||||
\huge\textbf{Zoltán Vörös}
|
||||
\end{flushright}
|
||||
\begin{flushright}
|
||||
\LARGE
|
||||
\vskip 2em
|
||||
with contributions by
|
||||
\vskip 2em
|
||||
\textbf{Roberto Colistete Jr.}
|
||||
\vskip 0.2em
|
||||
\textbf{Jeff Epler}
|
||||
\vskip 0.2em
|
||||
\textbf{Taku Fukada}
|
||||
\vskip 0.2em
|
||||
\textbf{Diego Elio Pettenò}
|
||||
\vskip 0.2em
|
||||
\textbf{Scott Shawcroft}
|
||||
\vskip 5em
|
||||
\today
|
||||
\end{flushright}
|
||||
\end{titlepage}
|
||||
'''%release
|
||||
|
||||
latex_elements = {
|
||||
'maketitle': latex_maketitle
|
||||
}
|
||||
|
||||
|
||||
master_doc = 'index'
|
||||
|
||||
author=u'Zoltán Vörös'
|
||||
|
|
@ -61,7 +94,19 @@ copyright=author
|
|||
language='en'
|
||||
|
||||
latex_documents = [
|
||||
(master_doc, 'ulab-manual.tex', 'Micropython ulab documentation',
|
||||
(master_doc, 'the-ulab-book.tex', 'The $\mu$lab book',
|
||||
'Zoltán Vörös', 'manual'),
|
||||
]
|
||||
|
||||
# Read the docs theme
|
||||
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
|
||||
if not on_rtd:
|
||||
try:
|
||||
import sphinx_rtd_theme
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), '.']
|
||||
except ImportError:
|
||||
html_theme = 'default'
|
||||
html_theme_path = ['.']
|
||||
else:
|
||||
html_theme_path = ['.']
|
||||
|
|
|
|||
|
|
@ -1,16 +1,36 @@
|
|||
|
||||
.. ulab-manual documentation master file, created by
|
||||
sphinx-quickstart on Sat Oct 19 12:48:00 2019.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to micropython-ulab's documentation!
|
||||
Welcome to the ulab book!
|
||||
=======================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
:caption: Introduction
|
||||
|
||||
ulab
|
||||
ulab-intro
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: User's guide:
|
||||
|
||||
ulab-ndarray
|
||||
numpy-functions
|
||||
numpy-universal
|
||||
numpy-fft
|
||||
numpy-linalg
|
||||
numpy-random
|
||||
scipy-integrate
|
||||
scipy-linalg
|
||||
scipy-optimize
|
||||
scipy-signal
|
||||
scipy-special
|
||||
ulab-utils
|
||||
ulab-tricks
|
||||
ulab-programming
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
|
|
|||
197
docs/manual/source/numpy-fft.rst
Normal file
197
docs/manual/source/numpy-fft.rst
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
|
||||
numpy.fft
|
||||
=========
|
||||
|
||||
Functions related to Fourier transforms can be called by prepending them
|
||||
with ``numpy.fft.``. The module defines the following two functions:
|
||||
|
||||
1. `numpy.fft.fft <#fft>`__
|
||||
2. `numpy.fft.ifft <#ifft>`__
|
||||
|
||||
``numpy``:
|
||||
https://docs.scipy.org/doc/numpy/reference/generated/numpy.fft.ifft.html
|
||||
|
||||
fft
|
||||
---
|
||||
|
||||
Since ``ulab``\ ’s ``ndarray`` does not support complex numbers, the
|
||||
invocation of the Fourier transform differs from that in ``numpy``. In
|
||||
``numpy``, you can simply pass an array or iterable to the function, and
|
||||
it will be treated as a complex array:
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in CPython
|
||||
|
||||
fft.fft([1, 2, 3, 4, 1, 2, 3, 4])
|
||||
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
array([20.+0.j, 0.+0.j, -4.+4.j, 0.+0.j, -4.+0.j, 0.+0.j, -4.-4.j,
|
||||
0.+0.j])
|
||||
|
||||
|
||||
|
||||
**WARNING:** The array returned is also complex, i.e., the real and
|
||||
imaginary components are cast together. In ``ulab``, the real and
|
||||
imaginary parts are treated separately: you have to pass two
|
||||
``ndarray``\ s to the function, although, the second argument is
|
||||
optional, in which case the imaginary part is assumed to be zero.
|
||||
|
||||
**WARNING:** The function, as opposed to ``numpy``, returns a 2-tuple,
|
||||
whose elements are two ``ndarray``\ s, holding the real and imaginary
|
||||
parts of the transform separately.
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
x = np.linspace(0, 10, num=1024)
|
||||
y = np.sin(x)
|
||||
z = np.zeros(len(x))
|
||||
|
||||
a, b = np.fft.fft(x)
|
||||
print('real part:\t', a)
|
||||
print('\nimaginary part:\t', b)
|
||||
|
||||
c, d = np.fft.fft(x, z)
|
||||
print('\nreal part:\t', c)
|
||||
print('\nimaginary part:\t', d)
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
real part: array([5119.996, -5.004663, -5.004798, ..., -5.005482, -5.005643, -5.006577], dtype=float)
|
||||
|
||||
imaginary part: array([0.0, 1631.333, 815.659, ..., -543.764, -815.6588, -1631.333], dtype=float)
|
||||
|
||||
real part: array([5119.996, -5.004663, -5.004798, ..., -5.005482, -5.005643, -5.006577], dtype=float)
|
||||
|
||||
imaginary part: array([0.0, 1631.333, 815.659, ..., -543.764, -815.6588, -1631.333], dtype=float)
|
||||
|
||||
|
||||
|
||||
ulab with complex support
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If the ``ULAB_SUPPORTS_COMPLEX``, and ``ULAB_FFT_IS_NUMPY_COMPATIBLE``
|
||||
pre-processor constants are set to 1 in
|
||||
`ulab.h <https://github.com/v923z/micropython-ulab/blob/master/code/ulab.h>`__
|
||||
as
|
||||
|
||||
.. code:: c
|
||||
|
||||
// Adds support for complex ndarrays
|
||||
#ifndef ULAB_SUPPORTS_COMPLEX
|
||||
#define ULAB_SUPPORTS_COMPLEX (1)
|
||||
#endif
|
||||
|
||||
.. code:: c
|
||||
|
||||
#ifndef ULAB_FFT_IS_NUMPY_COMPATIBLE
|
||||
#define ULAB_FFT_IS_NUMPY_COMPATIBLE (1)
|
||||
#endif
|
||||
|
||||
then the FFT routine will behave in a ``numpy``-compatible way: the
|
||||
single input array can either be real, in which case the imaginary part
|
||||
is assumed to be zero, or complex. The output is also complex.
|
||||
|
||||
While ``numpy``-compatibility might be a desired feature, it has one
|
||||
side effect, namely, the FFT routine consumes approx. 50% more RAM. The
|
||||
reason for this lies in the implementation details.
|
||||
|
||||
ifft
|
||||
----
|
||||
|
||||
The above-mentioned rules apply to the inverse Fourier transform. The
|
||||
inverse is also normalised by ``N``, the number of elements, as is
|
||||
customary in ``numpy``. With the normalisation, we can ascertain that
|
||||
the inverse of the transform is equal to the original array.
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
x = np.linspace(0, 10, num=1024)
|
||||
y = np.sin(x)
|
||||
|
||||
a, b = np.fft.fft(y)
|
||||
|
||||
print('original vector:\t', y)
|
||||
|
||||
y, z = np.fft.ifft(a, b)
|
||||
# the real part should be equal to y
|
||||
print('\nreal part of inverse:\t', y)
|
||||
# the imaginary part should be equal to zero
|
||||
print('\nimaginary part of inverse:\t', z)
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
original vector: array([0.0, 0.009775016, 0.0195491, ..., -0.5275068, -0.5357859, -0.5440139], dtype=float)
|
||||
|
||||
real part of inverse: array([-2.980232e-08, 0.0097754, 0.0195494, ..., -0.5275064, -0.5357857, -0.5440133], dtype=float)
|
||||
|
||||
imaginary part of inverse: array([-2.980232e-08, -1.451171e-07, 3.693752e-08, ..., 6.44871e-08, 9.34986e-08, 2.18336e-07], dtype=float)
|
||||
|
||||
|
||||
|
||||
Note that unlike in ``numpy``, the length of the array on which the
|
||||
Fourier transform is carried out must be a power of 2. If this is not
|
||||
the case, the function raises a ``ValueError`` exception.
|
||||
|
||||
ulab with complex support
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The ``fft.ifft`` function can also be made ``numpy``-compatible by
|
||||
setting the ``ULAB_SUPPORTS_COMPLEX``, and
|
||||
``ULAB_FFT_IS_NUMPY_COMPATIBLE`` pre-processor constants to 1.
|
||||
|
||||
Computation and storage costs
|
||||
-----------------------------
|
||||
|
||||
RAM
|
||||
~~~
|
||||
|
||||
The FFT routine of ``ulab`` calculates the transform in place. This
|
||||
means that beyond reserving space for the two ``ndarray``\ s that will
|
||||
be returned (the computation uses these two as intermediate storage
|
||||
space), only a handful of temporary variables, all floats or 32-bit
|
||||
integers, are required.
|
||||
|
||||
Speed of FFTs
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
A comment on the speed: a 1024-point transform implemented in python
|
||||
would cost around 90 ms, and 13 ms in assembly, if the code runs on the
|
||||
pyboard, v.1.1. You can gain a factor of four by moving to the D series
|
||||
https://github.com/peterhinch/micropython-fourier/blob/master/README.md#8-performance.
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
x = np.linspace(0, 10, num=1024)
|
||||
y = np.sin(x)
|
||||
|
||||
@timeit
|
||||
def np_fft(y):
|
||||
return np.fft.fft(y)
|
||||
|
||||
a, b = np_fft(y)
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
execution time: 1985 us
|
||||
|
||||
|
||||
|
||||
The C implementation runs in less than 2 ms on the pyboard (we have just
|
||||
measured that), and has been reported to run in under 0.8 ms on the D
|
||||
series board. That is an improvement of at least a factor of four.
|
||||
2195
docs/manual/source/numpy-functions.rst
Normal file
2195
docs/manual/source/numpy-functions.rst
Normal file
File diff suppressed because it is too large
Load diff
386
docs/manual/source/numpy-linalg.rst
Normal file
386
docs/manual/source/numpy-linalg.rst
Normal file
|
|
@ -0,0 +1,386 @@
|
|||
|
||||
numpy.linalg
|
||||
============
|
||||
|
||||
Functions in the ``linalg`` module can be called by prepending them by
|
||||
``numpy.linalg.``. The module defines the following seven functions:
|
||||
|
||||
1. `numpy.linalg.cholesky <#cholesky>`__
|
||||
2. `numpy.linalg.det <#det>`__
|
||||
3. `numpy.linalg.eig <#eig>`__
|
||||
4. `numpy.linalg.inv <#inv>`__
|
||||
5. `numpy.linalg.norm <#norm>`__
|
||||
6. `numpy.linalg.qr <#qr>`__
|
||||
|
||||
cholesky
|
||||
--------
|
||||
|
||||
``numpy``:
|
||||
https://docs.scipy.org/doc/numpy-1.17.0/reference/generated/numpy.linalg.cholesky.html
|
||||
|
||||
The function of the Cholesky decomposition takes a positive definite,
|
||||
symmetric square matrix as its single argument, and returns the *square
|
||||
root matrix* in the lower triangular form. If the input argument does
|
||||
not fulfill the positivity or symmetry condition, a ``ValueError`` is
|
||||
raised.
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
a = np.array([[25, 15, -5], [15, 18, 0], [-5, 0, 11]])
|
||||
print('a: ', a)
|
||||
print('\n' + '='*20 + '\nCholesky decomposition\n', np.linalg.cholesky(a))
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
a: array([[25.0, 15.0, -5.0],
|
||||
[15.0, 18.0, 0.0],
|
||||
[-5.0, 0.0, 11.0]], dtype=float)
|
||||
|
||||
====================
|
||||
Cholesky decomposition
|
||||
array([[5.0, 0.0, 0.0],
|
||||
[3.0, 3.0, 0.0],
|
||||
[-1.0, 1.0, 3.0]], dtype=float)
|
||||
|
||||
|
||||
|
||||
|
||||
det
|
||||
---
|
||||
|
||||
``numpy``:
|
||||
https://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.det.html
|
||||
|
||||
The ``det`` function takes a square matrix as its single argument, and
|
||||
calculates the determinant. The calculation is based on successive
|
||||
elimination of the matrix elements, and the return value is a float,
|
||||
even if the input array was of integer type.
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
a = np.array([[1, 2], [3, 4]], dtype=np.uint8)
|
||||
print(np.linalg.det(a))
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
-2.0
|
||||
|
||||
|
||||
|
||||
Benchmark
|
||||
~~~~~~~~~
|
||||
|
||||
Since the routine for calculating the determinant is pretty much the
|
||||
same as for finding the `inverse of a matrix <#inv>`__, the execution
|
||||
times are similar:
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
@timeit
|
||||
def matrix_det(m):
|
||||
return np.linalg.inv(m)
|
||||
|
||||
m = np.array([[1, 2, 3, 4, 5, 6, 7, 8], [0, 5, 6, 4, 5, 6, 4, 5],
|
||||
[0, 0, 9, 7, 8, 9, 7, 8], [0, 0, 0, 10, 11, 12, 11, 12],
|
||||
[0, 0, 0, 0, 4, 6, 7, 8], [0, 0, 0, 0, 0, 5, 6, 7],
|
||||
[0, 0, 0, 0, 0, 0, 7, 6], [0, 0, 0, 0, 0, 0, 0, 2]])
|
||||
|
||||
matrix_det(m)
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
execution time: 294 us
|
||||
|
||||
|
||||
|
||||
eig
|
||||
---
|
||||
|
||||
``numpy``:
|
||||
https://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.eig.html
|
||||
|
||||
The ``eig`` function calculates the eigenvalues and the eigenvectors of
|
||||
a real, symmetric square matrix. If the matrix is not symmetric, a
|
||||
``ValueError`` will be raised. The function takes a single argument, and
|
||||
returns a tuple with the eigenvalues, and eigenvectors. With the help of
|
||||
the eigenvectors, amongst other things, you can implement sophisticated
|
||||
stabilisation routines for robots.
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
a = np.array([[1, 2, 1, 4], [2, 5, 3, 5], [1, 3, 6, 1], [4, 5, 1, 7]], dtype=np.uint8)
|
||||
x, y = np.linalg.eig(a)
|
||||
print('eigenvectors of a:\n', y)
|
||||
print('\neigenvalues of a:\n', x)
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
eigenvectors of a:
|
||||
array([[0.8151560042509081, -0.4499411232970823, -0.1644660242574522, 0.3256141906686505],
|
||||
[0.2211334179893007, 0.7846992598235538, 0.08372081379922657, 0.5730077734355189],
|
||||
[-0.1340114162071679, -0.3100776411558949, 0.8742786816656, 0.3486109343758527],
|
||||
[-0.5183258053659028, -0.292663481927148, -0.4489749870391468, 0.6664142156731531]], dtype=float)
|
||||
|
||||
eigenvalues of a:
|
||||
array([-1.165288365404889, 0.8029365530314914, 5.585625756072663, 13.77672605630074], dtype=float)
|
||||
|
||||
|
||||
|
||||
|
||||
The same matrix diagonalised with ``numpy`` yields:
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in CPython
|
||||
|
||||
a = array([[1, 2, 1, 4], [2, 5, 3, 5], [1, 3, 6, 1], [4, 5, 1, 7]], dtype=np.uint8)
|
||||
x, y = eig(a)
|
||||
print('eigenvectors of a:\n', y)
|
||||
print('\neigenvalues of a:\n', x)
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
eigenvectors of a:
|
||||
[[ 0.32561419 0.815156 0.44994112 -0.16446602]
|
||||
[ 0.57300777 0.22113342 -0.78469926 0.08372081]
|
||||
[ 0.34861093 -0.13401142 0.31007764 0.87427868]
|
||||
[ 0.66641421 -0.51832581 0.29266348 -0.44897499]]
|
||||
|
||||
eigenvalues of a:
|
||||
[13.77672606 -1.16528837 0.80293655 5.58562576]
|
||||
|
||||
|
||||
When comparing results, we should keep two things in mind:
|
||||
|
||||
1. the eigenvalues and eigenvectors are not necessarily sorted in the
|
||||
same way
|
||||
2. an eigenvector can be multiplied by an arbitrary non-zero scalar, and
|
||||
it is still an eigenvector with the same eigenvalue. This is why all
|
||||
signs of the eigenvector belonging to 5.58, and 0.80 are flipped in
|
||||
``ulab`` with respect to ``numpy``. This difference, however, is of
|
||||
absolutely no consequence.
|
||||
|
||||
Computation expenses
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Since the function is based on `Givens
|
||||
rotations <https://en.wikipedia.org/wiki/Givens_rotation>`__ and runs
|
||||
till convergence is achieved, or till the maximum number of allowed
|
||||
rotations is exhausted, there is no universal estimate for the time
|
||||
required to find the eigenvalues. However, an order of magnitude can, at
|
||||
least, be guessed based on the measurement below:
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
@timeit
|
||||
def matrix_eig(a):
|
||||
return np.linalg.eig(a)
|
||||
|
||||
a = np.array([[1, 2, 1, 4], [2, 5, 3, 5], [1, 3, 6, 1], [4, 5, 1, 7]], dtype=np.uint8)
|
||||
|
||||
matrix_eig(a)
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
execution time: 111 us
|
||||
|
||||
|
||||
|
||||
inv
|
||||
---
|
||||
|
||||
``numpy``:
|
||||
https://docs.scipy.org/doc/numpy-1.17.0/reference/generated/numpy.linalg.inv.html
|
||||
|
||||
A square matrix, provided that it is not singular, can be inverted by
|
||||
calling the ``inv`` function that takes a single argument. The inversion
|
||||
is based on successive elimination of elements in the lower left
|
||||
triangle, and raises a ``ValueError`` exception, if the matrix turns out
|
||||
to be singular (i.e., one of the diagonal entries is zero).
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
m = np.array([[1, 2, 3, 4], [4, 5, 6, 4], [7, 8.6, 9, 4], [3, 4, 5, 6]])
|
||||
|
||||
print(np.linalg.inv(m))
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
array([[-2.166666666666667, 1.500000000000001, -0.8333333333333337, 1.0],
|
||||
[1.666666666666667, -3.333333333333335, 1.666666666666668, -0.0],
|
||||
[0.1666666666666666, 2.166666666666668, -0.8333333333333337, -1.0],
|
||||
[-0.1666666666666667, -0.3333333333333333, 0.0, 0.5]], dtype=float64)
|
||||
|
||||
|
||||
|
||||
|
||||
Computation expenses
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Note that the cost of inverting a matrix is approximately twice as many
|
||||
floats (RAM), as the number of entries in the original matrix, and
|
||||
approximately as many operations, as the number of entries. Here are a
|
||||
couple of numbers:
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
@timeit
|
||||
def invert_matrix(m):
|
||||
return np.linalg.inv(m)
|
||||
|
||||
m = np.array([[1, 2,], [4, 5]])
|
||||
print('2 by 2 matrix:')
|
||||
invert_matrix(m)
|
||||
|
||||
m = np.array([[1, 2, 3, 4], [4, 5, 6, 4], [7, 8.6, 9, 4], [3, 4, 5, 6]])
|
||||
print('\n4 by 4 matrix:')
|
||||
invert_matrix(m)
|
||||
|
||||
m = np.array([[1, 2, 3, 4, 5, 6, 7, 8], [0, 5, 6, 4, 5, 6, 4, 5],
|
||||
[0, 0, 9, 7, 8, 9, 7, 8], [0, 0, 0, 10, 11, 12, 11, 12],
|
||||
[0, 0, 0, 0, 4, 6, 7, 8], [0, 0, 0, 0, 0, 5, 6, 7],
|
||||
[0, 0, 0, 0, 0, 0, 7, 6], [0, 0, 0, 0, 0, 0, 0, 2]])
|
||||
print('\n8 by 8 matrix:')
|
||||
invert_matrix(m)
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
2 by 2 matrix:
|
||||
execution time: 65 us
|
||||
|
||||
4 by 4 matrix:
|
||||
execution time: 105 us
|
||||
|
||||
8 by 8 matrix:
|
||||
execution time: 299 us
|
||||
|
||||
|
||||
|
||||
The above-mentioned scaling is not obeyed strictly. The reason for the
|
||||
discrepancy is that the function call is still the same for all three
|
||||
cases: the input must be inspected, the output array must be created,
|
||||
and so on.
|
||||
|
||||
norm
|
||||
----
|
||||
|
||||
``numpy``:
|
||||
https://numpy.org/doc/stable/reference/generated/numpy.linalg.norm.html
|
||||
|
||||
The function takes a vector or matrix without options, and returns its
|
||||
2-norm, i.e., the square root of the sum of the square of the elements.
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
a = np.array([1, 2, 3, 4, 5])
|
||||
b = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
|
||||
|
||||
print('norm of a:', np.linalg.norm(a))
|
||||
print('norm of b:', np.linalg.norm(b))
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
norm of a: 7.416198487095663
|
||||
norm of b: 16.88194301613414
|
||||
|
||||
|
||||
|
||||
|
||||
qr
|
||||
--
|
||||
|
||||
``numpy``:
|
||||
https://numpy.org/doc/stable/reference/generated/numpy.linalg.qr.html
|
||||
|
||||
The function computes the QR decomposition of a matrix ``m`` of
|
||||
dimensions ``(M, N)``, i.e., it returns two such matrices, ``q``\ ’, and
|
||||
``r``, that ``m = qr``, where ``q`` is orthonormal, and ``r`` is upper
|
||||
triangular. In addition to the input matrix, which is the first
|
||||
positional argument, the function accepts the ``mode`` keyword argument
|
||||
with a default value of ``reduced``. If ``mode`` is ``reduced``, ``q``,
|
||||
and ``r`` are returned in the reduced representation. Otherwise, the
|
||||
outputs will have dimensions ``(M, M)``, and ``(M, N)``, respectively.
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
A = np.arange(6).reshape((3, 2))
|
||||
print('A: \n', A)
|
||||
|
||||
print('complete decomposition')
|
||||
q, r = np.linalg.qr(A, mode='complete')
|
||||
print('q: \n', q)
|
||||
print()
|
||||
print('r: \n', r)
|
||||
|
||||
print('\n\nreduced decomposition')
|
||||
q, r = np.linalg.qr(A, mode='reduced')
|
||||
print('q: \n', q)
|
||||
print()
|
||||
print('r: \n', r)
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
A:
|
||||
array([[0, 1],
|
||||
[2, 3],
|
||||
[4, 5]], dtype=int16)
|
||||
complete decomposition
|
||||
q:
|
||||
array([[0.0, -0.9128709291752768, 0.408248290463863],
|
||||
[-0.447213595499958, -0.3651483716701107, -0.8164965809277261],
|
||||
[-0.8944271909999159, 0.1825741858350553, 0.408248290463863]], dtype=float64)
|
||||
|
||||
r:
|
||||
array([[-4.47213595499958, -5.813776741499454],
|
||||
[0.0, -1.095445115010332],
|
||||
[0.0, 0.0]], dtype=float64)
|
||||
|
||||
|
||||
reduced decomposition
|
||||
q:
|
||||
array([[0.0, -0.9128709291752768],
|
||||
[-0.447213595499958, -0.3651483716701107],
|
||||
[-0.8944271909999159, 0.1825741858350553]], dtype=float64)
|
||||
|
||||
r:
|
||||
array([[-4.47213595499958, -5.813776741499454],
|
||||
[0.0, -1.095445115010332]], dtype=float64)
|
||||
|
||||
|
||||
|
||||
183
docs/manual/source/numpy-random.rst
Normal file
183
docs/manual/source/numpy-random.rst
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
|
||||
numpy.random
|
||||
============
|
||||
|
||||
Random numbers drawn specific distributions can be generated by
|
||||
instantiating a ``Generator`` object, and calling its methods. The
|
||||
module defines the following three functions:
|
||||
|
||||
1. `numpy.random.Generator.normal <#normal>`__
|
||||
2. `numpy.random.Generator.random <#random>`__
|
||||
3. `numpy.random.Generator.uniform <#uniform>`__
|
||||
|
||||
The ``Generator`` object, when instantiated, takes a single integer as
|
||||
its argument. This integer is the seed, which will be fed to the 32-bit
|
||||
or 64-bit routine. More details can be found under
|
||||
https://www.pcg-random.org/index.html. The generator is a standard
|
||||
``python`` object that keeps track of its state.
|
||||
|
||||
``numpy``: https://numpy.org/doc/stable/reference/random/index.html
|
||||
|
||||
normal
|
||||
------
|
||||
|
||||
A random set of number from the ``normal`` distribution can be generated
|
||||
by calling the generator’s ``normal`` method. The method takes three
|
||||
optional arguments, ``loc=0.0``, the centre of the distribution,
|
||||
``scale=1.0``, the width of the distribution, and ``size=None``, a tuple
|
||||
containing the shape of the returned array. In case ``size`` is
|
||||
``None``, a single floating point number is returned.
|
||||
|
||||
The ``normal`` method of the ``Generator`` object is based on the
|
||||
`Box-Muller
|
||||
transform <https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform>`__.
|
||||
|
||||
``numpy``:
|
||||
https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.normal.html
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
rng = np.random.Generator(123456)
|
||||
print(rng)
|
||||
|
||||
# return single number from a distribution of scale 1, and location 0
|
||||
print(rng.normal())
|
||||
|
||||
print(rng.normal(loc=20.0, scale=10.0, size=(3,3)))
|
||||
# same as above, with positional arguments
|
||||
print(rng.normal(20.0, 10.0, (3,3)))
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
Gnerator() at 0x7fa9dae05340
|
||||
-6.285246229407202
|
||||
array([[24.95816273705659, 15.2670302229426, 14.81001577336041],
|
||||
[20.17589833056986, 23.14539083787544, 26.37772041367461],
|
||||
[41.94894234387275, 37.11027030608206, 25.65889562100477]], dtype=float64)
|
||||
array([[21.52562779033434, 12.74685887865834, 24.08404670765186],
|
||||
[4.728112596365396, 7.667757906857082, 21.61576094228444],
|
||||
[2.432338873595267, 27.75945683572574, 5.730827584659245]], dtype=float64)
|
||||
|
||||
|
||||
|
||||
|
||||
random
|
||||
------
|
||||
|
||||
A random set of number from the uniform distribution in the interval [0,
|
||||
1] can be generated by calling the generator’s ``random`` method. The
|
||||
method takes two optional arguments, ``size=None``, a tuple containing
|
||||
the shape of the returned array, and ``out``. In case ``size`` is
|
||||
``None``, a single floating point number is returned.
|
||||
|
||||
``out`` can be used, if a floating point array is available. An
|
||||
exception will be raised, if the array is not of ``float`` ``dtype``, or
|
||||
if both ``size`` and ``out`` are supplied, and there is a conflict in
|
||||
their shapes.
|
||||
|
||||
If ``size`` is ``None``, a single floating point number will be
|
||||
returned.
|
||||
|
||||
``numpy``:
|
||||
https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.random.html
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
rng = np.random.Generator(123456)
|
||||
print(rng)
|
||||
|
||||
# returning new objects
|
||||
print(rng.random())
|
||||
print('\n', rng.random(size=(3,3)))
|
||||
|
||||
# supplying a buffer
|
||||
a = np.array(range(9), dtype=np.float).reshape((3,3))
|
||||
print('\nbuffer array before:\n', a)
|
||||
rng.random(out=a)
|
||||
print('\nbuffer array after:\n', a)
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
Gnerator() at 0x7f299de05340
|
||||
6.384615058863119e-11
|
||||
|
||||
array([[0.4348157846574171, 0.7906325931024071, 0.878697619856133],
|
||||
[0.8738606263361598, 0.4946080034142021, 0.7765890156101152],
|
||||
[0.1770783715717074, 0.02080447648492112, 0.1053837559005948]], dtype=float64)
|
||||
|
||||
buffer array before:
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[3.0, 4.0, 5.0],
|
||||
[6.0, 7.0, 8.0]], dtype=float64)
|
||||
|
||||
buffer array after:
|
||||
array([[0.8508024287393201, 0.9848489829156055, 0.7598167589604003],
|
||||
[0.782995698302952, 0.2866337782847831, 0.7915884498022229],
|
||||
[0.4614071706315902, 0.4792657443088592, 0.1581582066230718]], dtype=float64)
|
||||
|
||||
|
||||
|
||||
|
||||
uniform
|
||||
-------
|
||||
|
||||
``uniform`` is similar to ``random``, except that the interval over
|
||||
which the numbers are distributed can be specified, while the ``out``
|
||||
argument cannot. In addition to ``size`` specifying the shape of the
|
||||
output, ``low=0.0``, and ``high=1.0`` are accepted arguments. With the
|
||||
indicated defaults, ``uniform`` is identical to ``random``, which can be
|
||||
seen from the fact that the first 3-by-3 tensor below is the same as the
|
||||
one produced by ``rng.random(size=(3,3))`` above.
|
||||
|
||||
If ``size`` is ``None``, a single floating point number will be
|
||||
returned.
|
||||
|
||||
``numpy``:
|
||||
https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.uniform.html
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
rng = np.random.Generator(123456)
|
||||
print(rng)
|
||||
|
||||
print(rng.uniform())
|
||||
# returning numbers between 0, and 1
|
||||
print('\n', rng.uniform(size=(3,3)))
|
||||
|
||||
# returning numbers between 10, and 20
|
||||
print('\n', rng.uniform(low=10, high=20, size=(3,3)))
|
||||
|
||||
# same as above, without the keywords
|
||||
print('\n', rng.uniform(10, 20, (3,3)))
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
Gnerator() at 0x7f1891205340
|
||||
6.384615058863119e-11
|
||||
|
||||
array([[0.4348157846574171, 0.7906325931024071, 0.878697619856133],
|
||||
[0.8738606263361598, 0.4946080034142021, 0.7765890156101152],
|
||||
[0.1770783715717074, 0.02080447648492112, 0.1053837559005948]], dtype=float64)
|
||||
|
||||
array([[18.5080242873932, 19.84848982915605, 17.598167589604],
|
||||
[17.82995698302952, 12.86633778284783, 17.91588449802223],
|
||||
[14.6140717063159, 14.79265744308859, 11.58158206623072]], dtype=float64)
|
||||
|
||||
array([[14.3380400319162, 12.72487657409978, 15.77119643621117],
|
||||
[13.61835831436355, 18.96062889255558, 15.78847796795966],
|
||||
[12.59435855187034, 17.68262037443622, 14.77943040598734]], dtype=float64)
|
||||
|
||||
|
||||
|
||||
510
docs/manual/source/numpy-universal.rst
Normal file
510
docs/manual/source/numpy-universal.rst
Normal file
|
|
@ -0,0 +1,510 @@
|
|||
|
||||
Universal functions
|
||||
===================
|
||||
|
||||
Standard mathematical functions can be calculated on any scalar,
|
||||
scalar-valued iterable (ranges, lists, tuples containing numbers), and
|
||||
on ``ndarray``\ s without having to change the call signature. In all
|
||||
cases the functions return a new ``ndarray`` of typecode ``float``
|
||||
(since these functions usually generate float values, anyway). The only
|
||||
exceptions to this rule are the ``exp``, and ``sqrt`` functions, which,
|
||||
if ``ULAB_SUPPORTS_COMPLEX`` is set to 1 in
|
||||
`ulab.h <https://github.com/v923z/micropython-ulab/blob/master/code/ulab.h>`__,
|
||||
can return complex arrays, depending on the argument. All functions
|
||||
execute faster with ``ndarray`` arguments than with iterables, because
|
||||
the values of the input vector can be extracted faster.
|
||||
|
||||
At present, the following functions are supported (starred functions can
|
||||
operate on, or can return complex arrays):
|
||||
|
||||
``acos``, ``acosh``, ``arctan2``, ``around``, ``asin``, ``asinh``,
|
||||
``atan``, ``arctan2``, ``atanh``, ``ceil``, ``cos``, ``degrees``,
|
||||
``exp*``, ``expm1``, ``floor``, ``log``, ``log10``, ``log2``,
|
||||
``radians``, ``sin``, ``sinc``, ``sinh``, ``sqrt*``, ``tan``, ``tanh``.
|
||||
|
||||
These functions are applied element-wise to the arguments, thus, e.g.,
|
||||
the exponential of a matrix cannot be calculated in this way, only the
|
||||
exponential of the matrix entries.
|
||||
|
||||
In order to avoid repeated memory allocations, functions can take the
|
||||
``out=None`` optional argument, which must be a floating point
|
||||
``ndarray`` of the same size as the input ``array``. If these conditions
|
||||
are not fulfilled, and exception will be raised. If ``out=None``, a new
|
||||
array will be created upon each invocation of the function.
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
a = range(9)
|
||||
b = np.array(a)
|
||||
|
||||
# works with ranges, lists, tuples etc.
|
||||
print('a:\t', a)
|
||||
print('exp(a):\t', np.exp(a))
|
||||
|
||||
# with 1D arrays
|
||||
print('\n=============\nb:\n', b)
|
||||
print('exp(b):\n', np.exp(b))
|
||||
|
||||
# as well as with matrices
|
||||
c = np.array(range(9)).reshape((3, 3))
|
||||
print('\n=============\nc:\n', c)
|
||||
print('exp(c):\n', np.exp(c))
|
||||
|
||||
# using the `out` argument
|
||||
d = np.array(range(9)).reshape((3, 3))
|
||||
|
||||
print('\nd before invoking the function:\n', d)
|
||||
np.exp(c, out=d)
|
||||
print('\nd afteri nvoking the function:\n', d)
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
a: range(0, 9)
|
||||
exp(a): array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767, 54.59815003314424, 148.4131591025766, 403.4287934927351, 1096.633158428459, 2980.957987041728], dtype=float64)
|
||||
|
||||
=============
|
||||
b:
|
||||
array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0], dtype=float64)
|
||||
exp(b):
|
||||
array([1.0, 2.718281828459045, 7.38905609893065, 20.08553692318767, 54.59815003314424, 148.4131591025766, 403.4287934927351, 1096.633158428459, 2980.957987041728], dtype=float64)
|
||||
|
||||
=============
|
||||
c:
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[3.0, 4.0, 5.0],
|
||||
[6.0, 7.0, 8.0]], dtype=float64)
|
||||
exp(c):
|
||||
array([[1.0, 2.718281828459045, 7.38905609893065],
|
||||
[20.08553692318767, 54.59815003314424, 148.4131591025766],
|
||||
[403.4287934927351, 1096.633158428459, 2980.957987041728]], dtype=float64)
|
||||
|
||||
d before invoking the function:
|
||||
array([[0.0, 1.0, 2.0],
|
||||
[3.0, 4.0, 5.0],
|
||||
[6.0, 7.0, 8.0]], dtype=float64)
|
||||
|
||||
d afteri nvoking the function:
|
||||
array([[1.0, 2.718281828459045, 7.38905609893065],
|
||||
[20.08553692318767, 54.59815003314424, 148.4131591025766],
|
||||
[403.4287934927351, 1096.633158428459, 2980.957987041728]], dtype=float64)
|
||||
|
||||
|
||||
|
||||
|
||||
Computation expenses
|
||||
--------------------
|
||||
|
||||
The overhead for calculating with micropython iterables is quite
|
||||
significant: for the 1000 samples below, the difference is more than 800
|
||||
microseconds, because internally the function has to create the
|
||||
``ndarray`` for the output, has to fetch the iterable’s items of unknown
|
||||
type, and then convert them to floats. All these steps are skipped for
|
||||
``ndarray``\ s, because these pieces of information are already known.
|
||||
|
||||
Doing the same with ``list`` comprehension requires 30 times more time
|
||||
than with the ``ndarray``, which would become even more, if we converted
|
||||
the resulting list to an ``ndarray``.
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
import math
|
||||
|
||||
a = [0]*1000
|
||||
b = np.array(a)
|
||||
|
||||
@timeit
|
||||
def timed_vector(iterable):
|
||||
return np.exp(iterable)
|
||||
|
||||
@timeit
|
||||
def timed_list(iterable):
|
||||
return [math.exp(i) for i in iterable]
|
||||
|
||||
print('iterating over ndarray in ulab')
|
||||
timed_vector(b)
|
||||
|
||||
print('\niterating over list in ulab')
|
||||
timed_vector(a)
|
||||
|
||||
print('\niterating over list in python')
|
||||
timed_list(a)
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
iterating over ndarray in ulab
|
||||
execution time: 441 us
|
||||
|
||||
iterating over list in ulab
|
||||
execution time: 1266 us
|
||||
|
||||
iterating over list in python
|
||||
execution time: 11379 us
|
||||
|
||||
|
||||
|
||||
arctan2
|
||||
-------
|
||||
|
||||
``numpy``:
|
||||
https://docs.scipy.org/doc/numpy-1.17.0/reference/generated/numpy.arctan2.html
|
||||
|
||||
The two-argument inverse tangent function is also part of the ``vector``
|
||||
sub-module. The function implements broadcasting as discussed in the
|
||||
section on ``ndarray``\ s. Scalars (``micropython`` integers or floats)
|
||||
are also allowed.
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
a = np.array([1, 2.2, 33.33, 444.444])
|
||||
print('a:\n', a)
|
||||
print('\narctan2(a, 1.0)\n', np.arctan2(a, 1.0))
|
||||
print('\narctan2(1.0, a)\n', np.arctan2(1.0, a))
|
||||
print('\narctan2(a, a): \n', np.arctan2(a, a))
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
a:
|
||||
array([1.0, 2.2, 33.33, 444.444], dtype=float64)
|
||||
|
||||
arctan2(a, 1.0)
|
||||
array([0.7853981633974483, 1.14416883366802, 1.5408023243361, 1.568546328341769], dtype=float64)
|
||||
|
||||
arctan2(1.0, a)
|
||||
array([0.7853981633974483, 0.426627493126876, 0.02999400245879636, 0.002249998453127392], dtype=float64)
|
||||
|
||||
arctan2(a, a):
|
||||
array([0.7853981633974483, 0.7853981633974483, 0.7853981633974483, 0.7853981633974483], dtype=float64)
|
||||
|
||||
|
||||
|
||||
|
||||
around
|
||||
------
|
||||
|
||||
``numpy``:
|
||||
https://docs.scipy.org/doc/numpy-1.17.0/reference/generated/numpy.around.html
|
||||
|
||||
``numpy``\ ’s ``around`` function can also be found in the ``vector``
|
||||
sub-module. The function implements the ``decimals`` keyword argument
|
||||
with default value ``0``. The first argument must be an ``ndarray``. If
|
||||
this is not the case, the function raises a ``TypeError`` exception.
|
||||
Note that ``numpy`` accepts general iterables. The ``out`` keyword
|
||||
argument known from ``numpy`` is not accepted. The function always
|
||||
returns an ndarray of type ``mp_float_t``.
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
a = np.array([1, 2.2, 33.33, 444.444])
|
||||
print('a:\t\t', a)
|
||||
print('\ndecimals = 0\t', np.around(a, decimals=0))
|
||||
print('\ndecimals = 1\t', np.around(a, decimals=1))
|
||||
print('\ndecimals = -1\t', np.around(a, decimals=-1))
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
a: array([1.0, 2.2, 33.33, 444.444], dtype=float64)
|
||||
|
||||
decimals = 0 array([1.0, 2.0, 33.0, 444.0], dtype=float64)
|
||||
|
||||
decimals = 1 array([1.0, 2.2, 33.3, 444.4], dtype=float64)
|
||||
|
||||
decimals = -1 array([0.0, 0.0, 30.0, 440.0], dtype=float64)
|
||||
|
||||
|
||||
|
||||
|
||||
exp
|
||||
---
|
||||
|
||||
If ``ULAB_SUPPORTS_COMPLEX`` is set to 1 in
|
||||
`ulab.h <https://github.com/v923z/micropython-ulab/blob/master/code/ulab.h>`__,
|
||||
the exponential function can also take complex arrays as its argument,
|
||||
in which case the return value is also complex.
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
a = np.array([1, 2, 3])
|
||||
print('a:\t\t', a)
|
||||
print('exp(a):\t\t', np.exp(a))
|
||||
print()
|
||||
|
||||
b = np.array([1+1j, 2+2j, 3+3j], dtype=np.complex)
|
||||
print('b:\t\t', b)
|
||||
print('exp(b):\t\t', np.exp(b))
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
a: array([1.0, 2.0, 3.0], dtype=float64)
|
||||
exp(a): array([2.718281828459045, 7.38905609893065, 20.08553692318767], dtype=float64)
|
||||
|
||||
b: array([1.0+1.0j, 2.0+2.0j, 3.0+3.0j], dtype=complex)
|
||||
exp(b): array([1.468693939915885+2.287355287178842j, -3.074932320639359+6.71884969742825j, -19.88453084414699+2.834471132487004j], dtype=complex)
|
||||
|
||||
|
||||
|
||||
|
||||
sqrt
|
||||
----
|
||||
|
||||
If ``ULAB_SUPPORTS_COMPLEX`` is set to 1 in
|
||||
`ulab.h <https://github.com/v923z/micropython-ulab/blob/master/code/ulab.h>`__,
|
||||
the exponential function can also take complex arrays as its argument,
|
||||
in which case the return value is also complex. If the input is real,
|
||||
but the results might be complex, the user is supposed to specify the
|
||||
output ``dtype`` in the function call. Otherwise, the square roots of
|
||||
negative numbers will result in ``NaN``.
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
a = np.array([1, -1])
|
||||
print('a:\t\t', a)
|
||||
print('sqrt(a):\t\t', np.sqrt(a))
|
||||
print('sqrt(a):\t\t', np.sqrt(a, dtype=np.complex))
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
a: array([1.0, -1.0], dtype=float64)
|
||||
sqrt(a): array([1.0, nan], dtype=float64)
|
||||
sqrt(a): array([1.0+0.0j, 0.0+1.0j], dtype=complex)
|
||||
|
||||
|
||||
|
||||
|
||||
Vectorising generic python functions
|
||||
------------------------------------
|
||||
|
||||
``numpy``:
|
||||
https://numpy.org/doc/stable/reference/generated/numpy.vectorize.html
|
||||
|
||||
The examples above use factory functions. In fact, they are nothing but
|
||||
the vectorised versions of the standard mathematical functions.
|
||||
User-defined ``python`` functions can also be vectorised by help of
|
||||
``vectorize``. This function takes a positional argument, namely, the
|
||||
``python`` function that you want to vectorise, and a non-mandatory
|
||||
keyword argument, ``otypes``, which determines the ``dtype`` of the
|
||||
output array. The ``otypes`` must be ``None`` (default), or any of the
|
||||
``dtypes`` defined in ``ulab``. With ``None``, the output is
|
||||
automatically turned into a float array.
|
||||
|
||||
The return value of ``vectorize`` is a ``micropython`` object that can
|
||||
be called as a standard function, but which now accepts either a scalar,
|
||||
an ``ndarray``, or a generic ``micropython`` iterable as its sole
|
||||
argument. Note that the function that is to be vectorised must have a
|
||||
single argument.
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
def f(x):
|
||||
return x*x
|
||||
|
||||
vf = np.vectorize(f)
|
||||
|
||||
# calling with a scalar
|
||||
print('{:20}'.format('f on a scalar: '), vf(44.0))
|
||||
|
||||
# calling with an ndarray
|
||||
a = np.array([1, 2, 3, 4])
|
||||
print('{:20}'.format('f on an ndarray: '), vf(a))
|
||||
|
||||
# calling with a list
|
||||
print('{:20}'.format('f on a list: '), vf([2, 3, 4]))
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
f on a scalar: array([1936.0], dtype=float64)
|
||||
f on an ndarray: array([1.0, 4.0, 9.0, 16.0], dtype=float64)
|
||||
f on a list: array([4.0, 9.0, 16.0], dtype=float64)
|
||||
|
||||
|
||||
|
||||
|
||||
As mentioned, the ``dtype`` of the resulting ``ndarray`` can be
|
||||
specified via the ``otypes`` keyword. The value is bound to the function
|
||||
object that ``vectorize`` returns, therefore, if the same function is to
|
||||
be vectorised with different output types, then for each type a new
|
||||
function object must be created.
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
l = [1, 2, 3, 4]
|
||||
def f(x):
|
||||
return x*x
|
||||
|
||||
vf1 = np.vectorize(f, otypes=np.uint8)
|
||||
vf2 = np.vectorize(f, otypes=np.float)
|
||||
|
||||
print('{:20}'.format('output is uint8: '), vf1(l))
|
||||
print('{:20}'.format('output is float: '), vf2(l))
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
output is uint8: array([1, 4, 9, 16], dtype=uint8)
|
||||
output is float: array([1.0, 4.0, 9.0, 16.0], dtype=float64)
|
||||
|
||||
|
||||
|
||||
|
||||
The ``otypes`` keyword argument cannot be used for type coercion: if the
|
||||
function evaluates to a float, but ``otypes`` would dictate an integer
|
||||
type, an exception will be raised:
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
int_list = [1, 2, 3, 4]
|
||||
float_list = [1.0, 2.0, 3.0, 4.0]
|
||||
def f(x):
|
||||
return x*x
|
||||
|
||||
vf = np.vectorize(f, otypes=np.uint8)
|
||||
|
||||
print('{:20}'.format('integer list: '), vf(int_list))
|
||||
# this will raise a TypeError exception
|
||||
print(vf(float_list))
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
integer list: array([1, 4, 9, 16], dtype=uint8)
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "/dev/shm/micropython.py", line 14, in <module>
|
||||
TypeError: can't convert float to int
|
||||
|
||||
|
||||
|
||||
Benchmarks
|
||||
~~~~~~~~~~
|
||||
|
||||
It should be pointed out that the ``vectorize`` function produces the
|
||||
pseudo-vectorised version of the ``python`` function that is fed into
|
||||
it, i.e., on the C level, the same ``python`` function is called, with
|
||||
the all-encompassing ``mp_obj_t`` type arguments, and all that happens
|
||||
is that the ``for`` loop in ``[f(i) for i in iterable]`` runs purely in
|
||||
C. Since type checking and type conversion in ``f()`` is expensive, the
|
||||
speed-up is not so spectacular as when iterating over an ``ndarray``
|
||||
with a factory function: a gain of approximately 30% can be expected,
|
||||
when a native ``python`` type (e.g., ``list``) is returned by the
|
||||
function, and this becomes around 50% (a factor of 2), if conversion to
|
||||
an ``ndarray`` is also counted.
|
||||
|
||||
The following code snippet calculates the square of a 1000 numbers with
|
||||
the vectorised function (which returns an ``ndarray``), with ``list``
|
||||
comprehension, and with ``list`` comprehension followed by conversion to
|
||||
an ``ndarray``. For comparison, the execution time is measured also for
|
||||
the case, when the square is calculated entirely in ``ulab``.
|
||||
|
||||
.. code::
|
||||
|
||||
# code to be run in micropython
|
||||
|
||||
from ulab import numpy as np
|
||||
|
||||
def f(x):
|
||||
return x*x
|
||||
|
||||
vf = np.vectorize(f)
|
||||
|
||||
@timeit
|
||||
def timed_vectorised_square(iterable):
|
||||
return vf(iterable)
|
||||
|
||||
@timeit
|
||||
def timed_python_square(iterable):
|
||||
return [f(i) for i in iterable]
|
||||
|
||||
@timeit
|
||||
def timed_ndarray_square(iterable):
|
||||
return np.array([f(i) for i in iterable])
|
||||
|
||||
@timeit
|
||||
def timed_ulab_square(ndarray):
|
||||
return ndarray**2
|
||||
|
||||
print('vectorised function')
|
||||
squares = timed_vectorised_square(range(1000))
|
||||
|
||||
print('\nlist comprehension')
|
||||
squares = timed_python_square(range(1000))
|
||||
|
||||
print('\nlist comprehension + ndarray conversion')
|
||||
squares = timed_ndarray_square(range(1000))
|
||||
|
||||
print('\nsquaring an ndarray entirely in ulab')
|
||||
a = np.array(range(1000))
|
||||
squares = timed_ulab_square(a)
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
vectorised function
|
||||
execution time: 7237 us
|
||||
|
||||
list comprehension
|
||||
execution time: 10248 us
|
||||
|
||||
list comprehension + ndarray conversion
|
||||
execution time: 12562 us
|
||||
|
||||
squaring an ndarray entirely in ulab
|
||||
execution time: 560 us
|
||||
|
||||
|
||||
|
||||
From the comparisons above, it is obvious that ``python`` functions
|
||||
should only be vectorised, when the same effect cannot be gotten in
|
||||
``ulab`` only. However, although the time savings are not significant,
|
||||
there is still a good reason for caring about vectorised functions.
|
||||
Namely, user-defined ``python`` functions become universal, i.e., they
|
||||
can accept generic iterables as well as ``ndarray``\ s as their
|
||||
arguments. A vectorised function is still a one-liner, resulting in
|
||||
transparent and elegant code.
|
||||
|
||||
A final comment on this subject: the ``f(x)`` that we defined is a
|
||||
*generic* ``python`` function. This means that it is not required that
|
||||
it just crunches some numbers. It has to return a number object, but it
|
||||
can still access the hardware in the meantime. So, e.g.,
|
||||
|
||||
.. code:: python
|
||||
|
||||
|
||||
led = pyb.LED(2)
|
||||
|
||||
def f(x):
|
||||
if x < 100:
|
||||
led.toggle()
|
||||
return x*x
|
||||
|
||||
is perfectly valid code.
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue