From 60f910c87b249dc94729efaad76d529c82fca49c Mon Sep 17 00:00:00 2001 From: Andreas Dutzler Date: Fri, 7 Jun 2024 13:40:44 +0200 Subject: [PATCH 1/3] Update index.rst --- docs/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 9985f73..62ebd68 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,7 +10,7 @@ Highlights - Create functions in terms of Hyper-Dual Tensors - Evaluate the function, the gradient (jacobian) and the hessian of scalar-valued functions or functionals on given input arrays - Straight-forward definition of custom functions in variational-calculus notation -- Stable gradient and hessian of eigenvalues obtained from `eigvalsh` in case of repeated equal eigenvalues +- Stable gradient and hessian of eigenvalues obtained from ``eigvalsh`` in case of repeated equal eigenvalues Installation ------------ From 6a53e2026e4ca351c0c56f8bd63b96601060a988 Mon Sep 17 00:00:00 2001 From: Andreas Dutzler Date: Mon, 10 Jun 2024 00:54:09 +0200 Subject: [PATCH 2/3] Update CITATION.cff --- CITATION.cff | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index 796b72d..273676d 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -4,8 +4,8 @@ authors: orcid: "https://orcid.org/0000-0002-9383-9686" cff-version: 1.2.0 message: "If you use this software, please cite it using these metadata." -title: "tensortrax: Math on (Hyper-Dual) Tensors with Trailing Axes." +title: "tensortrax: Differentiable Tensors based on NumPy Arrays." type: software doi: 10.5281/zenodo.7384105 url: https://github.com/adtzlr/tensortrax -license: GPL-3.0 \ No newline at end of file +license: GPL-3.0 From 23253829c37a1782e0489bb36e667863ed764314 Mon Sep 17 00:00:00 2001 From: Andreas Dutzler Date: Mon, 10 Jun 2024 13:12:59 +0200 Subject: [PATCH 3/3] Revert "Merge branch 'main' of https://github.com/adtzlr/tensortrax" This reverts commit 664f2529eab0adf84a50f91e10b96c8c98178314, reversing changes made to 60f910c87b249dc94729efaad76d529c82fca49c. --- ...upload-codecov.yml => upload-coverage.yml} | 2 +- CITATION.cff | 4 +- LICENSE | 4 +- README.md | 7 +- docs/_static/logo.png | Bin 9880 -> 0 bytes docs/_static/logo_inkscape.svg | 195 ---------- docs/_static/logo_without_text.png | Bin 1751 -> 0 bytes docs/_static/logo_without_text_hires.png | Bin 4158 -> 0 bytes docs/conf.py | 6 +- docs/index.rst | 43 +- docs/knowledge.rst | 11 - docs/knowledge/derivative.rst | 75 ---- docs/knowledge/tensor.rst | 55 --- docs/logo/logo.png | Bin 0 -> 25253 bytes docs/logo/logo.svg | 105 +++++ docs/tensortrax.rst | 4 +- docs/tensortrax/evaluate.rst | 37 -- docs/tensortrax/math.rst | 60 --- docs/tensortrax/math/linalg.rst | 22 -- docs/tensortrax/math/special.rst | 25 -- examples/ex01_usage.py | 23 +- examples/ex02_numeric_variation.py | 62 --- examples/ex03_custom_extension.py | 33 -- pyproject.toml | 2 +- src/tensortrax/__about__.py | 2 +- src/tensortrax/_evaluate.py | 367 +----------------- src/tensortrax/_tensor.py | 8 - src/tensortrax/math/__init__.py | 4 + src/tensortrax/math/_math_tensor.py | 14 - src/tensortrax/math/linalg/__init__.py | 4 + src/tensortrax/math/special/__init__.py | 4 + tox.ini | 4 +- 32 files changed, 153 insertions(+), 1029 deletions(-) rename .github/workflows/{upload-codecov.yml => upload-coverage.yml} (90%) delete mode 100644 docs/_static/logo.png delete mode 100644 docs/_static/logo_inkscape.svg delete mode 100644 docs/_static/logo_without_text.png delete mode 100644 docs/_static/logo_without_text_hires.png delete mode 100644 docs/knowledge.rst delete mode 100644 docs/knowledge/derivative.rst delete mode 100644 docs/knowledge/tensor.rst create mode 100644 docs/logo/logo.png create mode 100644 docs/logo/logo.svg delete mode 100644 docs/tensortrax/evaluate.rst delete mode 100644 docs/tensortrax/math.rst delete mode 100644 docs/tensortrax/math/linalg.rst delete mode 100644 docs/tensortrax/math/special.rst delete mode 100644 examples/ex02_numeric_variation.py delete mode 100644 examples/ex03_custom_extension.py diff --git a/.github/workflows/upload-codecov.yml b/.github/workflows/upload-coverage.yml similarity index 90% rename from .github/workflows/upload-codecov.yml rename to .github/workflows/upload-coverage.yml index 912aadc..b3b168a 100644 --- a/.github/workflows/upload-codecov.yml +++ b/.github/workflows/upload-coverage.yml @@ -23,7 +23,7 @@ jobs: - name: Test with tox run: | pip install tox - tox -- --cov tensortrax --cov-report xml --cov-report term + tox - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 if: ${{ matrix.python-version == '3.12' }} diff --git a/CITATION.cff b/CITATION.cff index 273676d..796b72d 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -4,8 +4,8 @@ authors: orcid: "https://orcid.org/0000-0002-9383-9686" cff-version: 1.2.0 message: "If you use this software, please cite it using these metadata." -title: "tensortrax: Differentiable Tensors based on NumPy Arrays." +title: "tensortrax: Math on (Hyper-Dual) Tensors with Trailing Axes." type: software doi: 10.5281/zenodo.7384105 url: https://github.com/adtzlr/tensortrax -license: GPL-3.0 +license: GPL-3.0 \ No newline at end of file diff --git a/LICENSE b/LICENSE index 80e12cc..daab826 100644 --- a/LICENSE +++ b/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - Copyright (C) 2022-2024 Andreas Dutzler + Copyright (C) 2023 Andreas Dutzler This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - tensortrax Copyright (C) 2022-2024 Andreas Dutzler + tensortrax Copyright (C) 2023 Andreas Dutzler This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/README.md b/README.md index 74b91b8..079911e 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,12 @@

- -

Differentiable Tensors based on NumPy Arrays.

+ +

Math on (Hyper-Dual) Tensors with Trailing Axes.

[![PyPI version shields.io](https://img.shields.io/pypi/v/tensortrax.svg)](https://pypi.python.org/pypi/tensortrax/) [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) [![Documentation Status](https://readthedocs.org/projects/tensortrax/badge/?version=latest)](https://tensortrax.readthedocs.io/en/latest/?badge=latest) ![PyPI - Downloads](https://img.shields.io/pypi/dm/tensortrax) ![Codestyle black](https://img.shields.io/badge/code%20style-black-black) [![DOI](https://zenodo.org/badge/570708066.svg)](https://zenodo.org/badge/latestdoi/570708066) [![codecov](https://codecov.io/github/adtzlr/tensortrax/branch/main/graph/badge.svg?token=7DTH0HKYO9)](https://codecov.io/github/adtzlr/tensortrax) # Highlights -- Write differentiable code with Tensors based on NumPy arrays -- Efficient evaluation of batches by elementwise-operating trailing axes +- Designed to operate on input arrays with (elementwise-operating) trailing axes - Essential vector/tensor Hyper-Dual number math, including limited support for `einsum` (restricted to max. three operands) - Math is limited but similar to NumPy, try to use `import tensortrax.math as tm` instead of `import numpy as np` inside functions to be differentiated - Forward Mode Automatic Differentiation (AD) using Hyper-Dual Tensors, up to second order derivatives diff --git a/docs/_static/logo.png b/docs/_static/logo.png deleted file mode 100644 index 5663d70f60336f18f7579ded52fe834d7849cfb7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9880 zcmYjXbzD>b_r5gJ14KF|f|D-k5GFBtNJ&TzknToQVkoK9MhQ4V6a=JGkscs|AV?`N zx&@@Ud?@*G1-)3_P zC~$GbN88dL0I1RbK19pOFdc9sTfkk5026OiK(M`^GY}jcEb8Xv?(b;t<1Fg!=aRFn z#0~(wfDZi5y^!3kyzmfC)05tgov9&#HDw$Ui)6TaQ^e0rmu#to(hqVgmhxHmhbj4& zq{ZZSn?fv^8JOR)x=GdkxDO|NYYncEw(c#O#Sn`yGr((clx?N`+r#u2#DvIhbTs7wh>#x*C!d%Gg{4m~Um zG4r-$3xE7#s)aXRYH^1ynE@fEYo%gjK0)$}fL!o!u6$L-)MPJcE`m4y%ym2@&qif| zrw}fAJT7hVjq=b-Nx@R8I@kAA`g&G8`pB73(_>GV_B%cK1KQ>c8bPm}OUI%*`0o_G zrTh1+%m3T+hvr&4@?a0NDQ8yWl22?Ym#%aVqP`54F>VyFdD%z&vmgb{bR_lt*+X6# zx@S#cRuK<(vSD2!FH%4oG5Fjkt$It~WBK||macy%cUf_8x+TN&&k7`WGqC?1CE6ug zK3>uqG;)V?>XCP5ZAF|F%YR24r$Cl(B4KYDyt2uKI=@%soU~AQ#FgDov8~Rp42>G| z86+%%CKa&x0`ZQgo79La-87yTwcAj7{A8TG?6*}}C3igY*38ZW++1PBL?CSg>pzcQ zkIg$P+-9pJ8f$;G(5~$WBQ<3AN+6G=3?g=A^OE@YRGL&fFd0Tzv`M~}9lyV=P<`hu zn`CjKJ2R>t3VEWzoOj+1tTiZpotQAo)8&w{vjYexN%WD>(%$5riFc>&$n8uN$m-o0}a9t`7L6_XE$cf-=| zUdE_QNBNr~;+Q+KK^H-;sINp90TBcmOS$8K5fg9#4+GG!N(S@yGOHSZ- zMHd!pne44ZM28+u3o%eylfDXUblTrD=ss_)f;;hds-!9Feu>C` zuQ~#O+h@BQvztfr-&$O(jii3&f4JOEs2cxoTM%0W?mwi}4lW4&hu#94xLM)j;t}n_ zrlymH00m`pN?*!o7;yTv#>1W<3XDKT9pmgJqDA1pC8Jl=3%@vWadBA* zh0Gem1LBZx>fgLse|$qXvvoPhHat9h>uBqTA%Br$f-8Bn4gz)u&XKSxf*aQ;zqj6g zRmiM$XKsp?mbNf~NihG4od+*Nh-?3g8<)O{RUq=38yb#Dp$bHuG{8&X9%NJ#H%<WNEJfr&dxVvxw=}iQj-U-Qo|lm#68NrESzo@!KrvRW7#++SZ`ch%C;`m`ZqWo31Y zz#&H%Mw!p<(*ts}mgSwFe(pv+R$Jh}pOXPhHO_tmy?9De;#6cMU(qm^Vvumh2_dJY zZNQZBOvMdgnWiV3JUCr)Kv`sFyg<%rz;$W5{`ljZnsz@?AHNr_h{dQl8c zq0ws2?CdN%Bxxk`hLtV!Ax*4$j*PxLTCz47O*%W9-DgS8SYKAj(~(+1on4%glU{-V#Q&lK>L6 zU!~uPuqTPaT!dv`r)NnVWB@nw41(jkeqMK|{yyEl*nZ+Gt2TKk4?e>s`TP5ndja!2 zQY*MidxCq-i0}D3qW21%bm3s&;@*A@y!Zh+J`mbWyqj*Ye|s9K%aB)0 z&@}Xyz&3eisaZ=IN{0g6!x2I>GX=bj7~Rx&5_YsPYg@i$QpfdNpw&9)^18e1 zLf+6yAtkJ^b0$}3%ut~pOD*-*%5p!^BZ;4&L#*79?9B%ojxTaRjOlY z+ZB)-8oos4TY6bC;KmE_5ggI26v_HjogIKjz4HtS=G)wNqPV;fe7M;C5344&$NaC; zuR*2G0+DIs zOB_uLFakY5cAOqAUj3pSgbSUUr>F9}FHtY9y-ruXYae?g8a;^#WrEyDD9glb0EvP{ z=Gjd)Fqx)Q%LY9qL#NG$KrHh#3K}*PMc;5r-Ko}-e8nQ*QVAuF43ysE#3}S_F=ZS7ugV^PXp9;VO(p1mkJO|5(tu*bW&)WPB zS2?jeQC#8sOA}oCMHe68d6T($rH8{iqCxGl zUmZ;jWC7XhauJ+TRUWRca)HYO8TszpkkPt6*57OpZ-ew=nefe$VnP7lkeudnw7(ddL5?>>4v z%|~|upFGT>?FxC7&DmTjE8)+)E!3?K(4g9t*^^W@COQfW`R~Ja~W#d45^?Cn0!1 zVHO=eLULcf*%x{r0T14(o3wRr-M$;w_0y2Jksgy#{E{1b8jD=d1cwEc?b|_)j*j{_ znH6Hu*zA*lrAPEAqCfHJ`-|PoId0euT>1*pA11#7+1|jwKpX0X4cj>@enyCmjZFu% zh+dJdts))E^Ss;d=udDsu3kX`qI;WrseGi|@$d7IgZjYi$sr@{$5o9x5)_k_s5^-oIgkk}oPHA~uuM$2T(aV&wUS zt7G+dazNGF!y_?Py|9AlgA0#f(Mth?+&?5hRtBHL5s5ibBOgXDsskOG=qXmY@;Y7i zyXxN!qC~&k;DbOF@<7!P=;6z^q!Nr&!lKaihlA2Qda?K}n%f z6;i1OPZIt0?gZGV_Rod;s`Pz@e~@FQx_FhXRjhoak-jtXr_UcMzL*5Ot-AGHM^~iA$K1 z*_t^=oN*?JnZN(RM?|^e&!R8r)y#Xh4u>3=5JADD*jdXhW%`_4Hn{ z(aiwb=nJl)oGpq&N86s7M;_PQut!fk**6~TN*>5c>$lusPinU)DO1LuH-V|8D$Ah$ z*u_PTGYW4uccx&FAT1+vftspuz7(}@^yjP+fnr^-glMWEBG4&T()v~|ii&#cs-2tG zzJxF1y0ax$Bq;`Fkdkh$rtDy*df zMqjms$2ZVpCx zImtpA+}R$e8K|ExxZjid3_UEP|KBc_=kF#0FQidXG)?pw#vt`@U=H>9A-W+yP=L8~ z!V<84O9`6AhOu2gn;xs#!K8RdKyq?ApceV$9qQl*EuvAC}8(2?ZbcNu-fXpFaJJM0WK zCVlPN`efC(ZRm2wx`GqN zpaH&`7L%;0l?flX-fWV&l5tc)Mnj^G*2T!L=xW2WGNcxR9k2vbY)Rw*ox#@P7zlQX z;Gm%P1!rOCx;V{Z{>K3))*g17$-p0b{5i46fk6sCROrEw>@k?shesY77YN4#!q6i@=*V4ljY1+j6`UW2{2>(iUXwJKiXr)V5mL@ z^$6-@OK~X4`{&wAI$Yk{%U5Oko=&1p&s?KAo zCJCQ=%wSkLM%|Wt7oHLbV#J26KQ3uSe=NdNoJ3k$niPoWAI%kj8s^91?0PdVzAPIx zj!P7cuI^x_L_C(Yf^Mp-A_u&AFIzCsqBx|wx@Int1bFr~@*`Dp`I1KO%Eixs-i;y0 zp2QPRoY*J)@X(H1b7j`%rvd!(_A)aZwv5{L4NGbIc^;vOLzaUX->SDU--gd4HfOO( z2QHiO()>MhSa`T&-%G_SUx$zU;c}q4~ocw}YKrh>e#&59`bRmtIB@2|5 zM)arTsffk<&b211RB$YgqCd%RGzGkNa{9o!0&2~CQ|i%>c;bt58O_&7lIZa= zW$VSP2u*=BZFu8ngz_fefmT1mXxH#<+&G`t3d<{3pxk`3P1r?g>bEw()oUFO#am~kt{mqqrC%?=eC$YRm`%+wqoZ#)E zSK(QNCneh-!Rr3S7XCb3>!8fISd+_UofW(|DC!)f>3F2-w>eujOzG5mYqZz7V6tY? zuA7ygY+8R_+`CBSsXhE`WL?qT{x=Yiw>1eu9lc;s4>3rzh7Mc2!iFfR*!k?#I1k#2 z*hwk?o!-CpLb6KSB=pBv!1VXj=rQL@Kn*@@>a;UnZX(qFBrvrB>x>eU7k#W}J!wD- zp=CuGLx<0L@z&K&6J89)bAQ>Sc($EselSPg-miVnNxx9gN2OI=brPblpH#YVG^_Gw zx>S;3G~T+Vp+F2LJUHdT`)f}wb!Wk-d?xcL-$?VTboS?ZZYfV#<3?dP&Lq9gdK0%T z9k@P#MN=XN^Z{_tj0+1g-@rxCU_2N)L~qCz=o{Q6MpZWI zOz4QSCR&+Q&N-pPG9VFiU7?o)2Fhl)z#_gQ0bUtpjQDm{E>aCt2r^3C{1eLFb;7Pd z6duQx$GA7mxD~YFtU{eVt3UnY4?6e*oA_^k9@uUyW@j76M^7KHQAU zE4q4r&E%)#&d=`B!7`njaMQPNPKkypi!~2gd5)`!iyb*QLuC@mHg&)|I_IY1w{js+ zfS2v?>p9H88s6nCn)~HK%a#+vaQCI~4o!9mMv`XHyu@wTfj~kO!PKeY6Ow=R6?-)4 z`}>HKSard`fB^oq{?I9Ump2ylFa)e&CZ_A8Z3^RyT~Z27%hUh~tIYn;Vm?#Q_f%&~ z0l-|B95OT}y>;$KKDRZLLJ?EH@Po%kU&%IgGt9L#@q|8^ZEFbQHqsgX@?~bT%=^{| zG4e8IWqjihvzP;_Au|2hM|=WU&aXVXF*D0n&BT8B{qJ>92_Uv(fp8`3-HtC_u-$f6 zina={nI9BSENJ9(ahlmUbD4^iX^(rM#``{gu)Tt(*@W9FwPir$o@Tkj`1tr{;k~I? zyg$eh7@>Xy<)O!D8rbr~F==IGji7LxnJ!BS*`Piv-8k8cGL4gAJ(DL_k3)D^(viL^ zQH)aFDWKp&`%=4w-9xA;5bz?Ay!yu7-Q7?_fbwXml@31%60(QV?19Y=B%P1aTEwKv z9!}kO5lo8;5~)};Nf!mCPy`aNFd<$}cer&GmoC+%Wx3iM^~}at*(C(1BIy$qlVb1W zLJnMSmHW7PgxJ~&5HUN{yA|cY!b9iTO%$}2Et7ENPw+ra%BS9k^F5!{dct-mxxx#r zn!XYIwHOx1iZ3_T!geTRWMtf#5@KR!2Iy^P>mrrxdZXTvzW-WjaY4PL!i+{MO#k@7 z6OeGtI!VvIPelMce9Jov#(rzxkBo{hpX1ZU*1%g$6mCvy=+E9FJ&Aa_PQGrkju70$ zWb7~g+`VO8vNeA{I?^|5>){t+l1`?rOM zRF)nUewm2E9T^?p8#x@mRW`og&X2b?`+?`jV|*zzoXTcd@Ro| z^bw)_(Y>h~%1tB5T#5!{J}yTm_;Y^9@@5Me@#9dSlIIp1CN#e`0iDnNW+U_lD^1N< ztJ(IBeOa}Jf*<1<6(7OgHDmhxV?g~&=K0vSoG)%4f18|qq{^6Z6U2}qoSs*A4Kk_( zTW$a&yCi+%PcpPcHq?1F!8P|s0PsFsB@Sxw)_yAQ zJ<^dEQ^eV!p5>7JC#K@5Gq)eVA_eZ@8n)OuOf_hvIyPn~t{2f~DBAKeG@8Jc&($@d zX?+^DgJWmu@ixAHY@Dv1I}?;l;KD57He(-3Im?IWR@c{0!I-%v%p&1$TjT^~ulZoT zEFQYQztbFPnkf$PZD@$Omn95GP^wp;;pr zpSSd#lOA&4`c>rOEJKlfG6C9D_Y3x8p*negLI~n65)Cfzy?))BEAI~~a2n++2e`3g zH8n2kFJ9hAk*j>4@0ZH5OFg3MbY4!JoSHf@ge^Zvvl84W3sGvPRD0`jC$hEXswE-` zO{Fhc6@UE!FGR%4%PZ>r3(Rhy&4bVQUPWw!T}-4PsG!}zS;x=35HtaWr#oOBvgOln zN(sG>#~1i~xOiV)zShoIQJEE!UULN_ZRT9)HeC*15@x1G(PV_cP9@uWc01d-KHa(E6w@yEI_4%E81YcD*uTg2>Eeh0d1zaTN&$>?^lBP?1n`|Lbi~SlUX;g#jXwfsL?`~dR z`P8pA!_fNr`e#g|B7KJL$1h!_*M;ByFx1lOHG^X5wUW&N9$kJ$vI^U-(84^fXRSxK zW5S4e=f_QngZhuc^S0nl(}(p+Oi7wM6h86Aq0Erk3b$#1B;7+* zov{za8OTR!QPEq?5{^AI&DsPbkXkiK$K%oqeB5}vvN1P+75LvTplqkA18ZvvRD8cP ze{qw6*Cr9Xmc(PV&LN4Vw11qd!s1bv_=LDW)BbT3nBRlVJa$~NkPiX1w5&zDL(_X7&}dt zZ#wo~4E{|H)A3QubbhDS?%4MzhG8WGtE9-D^p{-CmY4{&Jsvn)XZusM=@M@KZsV73 zNtHT-f1FijTCy9~5L9@DAdxC9d6<-UZ{FMZ@mH)~Pu?Wpm7$6|Lf>JaFypDqp{S3ru}}y@!J6+!Ar32t7@+ zXH9X8wx|L%0wYi!Y9&6d%0jb9){apvK1<(60xpVJCUvxb>-OXXcvIltWqufwywAk! zxPltB!`fF|PR+>tjSP0+i;P&?$280xi%cHCI>zYccqRfzfXez@>vPI&)T8ue17450 zkv_en4SPW5Q|X2sVP=;Cr2w#fbP&T3{OKpl5WbdZ29nY{KPtUt4E6kcg$w}5+5XK1 zIHiDi+7W^=@_-#9B$BoBN+0Pai=>o_#qb%|71p@DT$`vawRR|$_|^3c#4Il^g1mDc zz1jiH86yIz`JUjFu5|td2*h$P&@*pE-QQGHh`|KFpf%pw+WMz6U$SEo)Nd1ahN`0v zHqZXvj9$Tn9%J&PtBK1 zWK{O~oIgCM>HDzct`_u^1aiUHd7Qe}=x5@p2bWFcRt~dI*iY7`>9Wv-yL`ma( z6l#i64@U5^kWufg^-fiaT^XkurN)co2jo5v%jG4miP_H7x{a0iqgNG{cB&7nRV86f zUc5zCo|az3K364c_tmL9Nu&x`k~Fijpo7}4!1`z7X4{M3u7J5amhjg2xV<`L?^pim z>1izyjnj)u-ssr(kG$c(t)QnjAU2+tbhF#@7g@38YsjB(m*bVPv&*Tq=#zS zZ2oDWxN&(Pm(QtSZGC;9dSde{f)-dEFrR?lv}N~_*HqBW5{+c*>`mzVR^A!=;@1?| z+j{_ZujQFBZ7m47-Bw@$-X-2{_N!gbmL&wm{pKRK)g7$oh^m}fdz97D8bmXR0}(2K zPdG1w+?(vF(f)x9p|Lo7Ymc4MHYAZeYiAXhQc;;NNybH1zz1n}23HzIMbCazJH7J`*EGDsC5j z-MUqP+8309f^$kvOrclOdEB{mR_1)QG8T0bKl;2%;v-b4$v-6u+KZ@!I@FG+m8)utQ%|6a&c$_Y4CK`GKB$ zTU%Qdao?44I|5%MeVqNwQ~aA#Xu4NqKLG+&_$EChRr@?(n$6uWN^y860xX)Y`MJOL zG(zM_8Chel>5W+<;6j{|OBRcoPPVm$PdcO`Z)Z!@mV>fjv`*EH-e1Q*R9D?{n`FEf z7mFq5$FzdR7pH7XpH6m4C!Sq9qC-8rU->-(xLy8Lm+y*ryTV@V|4rCnF}x<0rp*rp zTeZn#K1z@w;5`N8o5TSMNCD>sY?!~bFrIijb+oOBK^r&_G*V&u1zS&V7zieA|Io}vQMTT9}!bLFL8Sgtvt=zvcAjO4gtG^C#q6S~&CA)Dz-kBhjg`Op(n*c>)SHK0xPD+$@I6^!lYP|y&lRPNKM;I0!69NB74cN9B#Dua(t9ng; zDDPV1FWxxSL6lP`lxe{ZFAx-!4Rv*E|D=_Hc8xV&VNH6@WraKX6Bpa2iMW>U_;x^(9m$i=aD9uXzM^E$aHO;te9{o zB_%QSBy&{+bt}ZQWPk!xvKnvXf-%G6mY@Gks_;4d9jPc2E8XcLMKn diff --git a/docs/_static/logo_inkscape.svg b/docs/_static/logo_inkscape.svg deleted file mode 100644 index 4a0b211..0000000 --- a/docs/_static/logo_inkscape.svg +++ /dev/null @@ -1,195 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - tensortrax - - diff --git a/docs/_static/logo_without_text.png b/docs/_static/logo_without_text.png deleted file mode 100644 index a0cbbc55da86d6c4e82edeea2c876b08c4ced5e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1751 zcmV;|1}OQ7P)U?ZMLR}JNQ(BcYC5583`Pi^XtyWq-BzOUV`hdg(;spr_ z2?=OHSSqxOctHXQBqSsR4`};95ieBQ6|5Gq&?cqOK$=!SKWJ$ZNaG}RV|zT82RDvY zQrbIlVtZzOPjfwUj?c_r_xk#rdj|pn0%{MLJJ2umy@Swa?7`6)3ieZk!)IvAjt=&} z{|eCWCYb5z>D#t!*3=Hf5?snq`xGR}V{^HWMZVAmFI@hBmVeCPeL8Keo?q#00hQyu^ z9!=`hx)Tr`8?1IPbx2K#DNeo{+v-FWbR;u=O|D^OwL_{aYIB%%w5WpZ%Nglns7)~g zToH)rAv`JO>VY?I(5ZD@I<>i`<^)TPCvP@9JJF48HKRrgdi#c75oZ)TGA92#bhg^D zEhU~5vxMVV2yq}zD2INoCxfU_9On$-NIL~Bm-SK0Sou?uI#S9(dt1!kAq{A~29Y&r zXLtSo$XHJTugnq`F*|4nL0N>}D*>^r}WSWePb6sQ#fd6Gy@l(FZ$o{_O<1{K8fPYP`H5%<(tQ_|= zoh&v)KPjo<1$yQpkR68)>1`XrC}(XY{4FP^?FK&0bQsxmgfk^E56!mmb++Be{vS=J zXrw6Sr{_8-81$)*(D`{WFQO#o{{8zOP)e;bimFI1kx0BHJBJVG?d#P~n;yPD*ARD= zhg&=N+FtTG4RC71LpNqyx;owX={orKCz08+SvR)zOnJ|IT5Fwj;bR7P3xJnK27mqh z_Sfx5^Q~@zDM<9+7C9SWvh(IGv&|iUbK_^_pZ0#~M=>ippA*n_xG7ypp8;1+QlOu-~&PG|R2AaWfn1eCc?7 zbcz+qtV$D=`C`y5v2h%O+-$3L>BN@r#q5cs>4~^cTK&E8)S|xsk=7%NvM#llH8-;4QKCeC~5>x^u+wtUa#hMxg=bE4O{ z1hz9iK)W>yz!PWsjZ0vul?$);FNvv?dLA%~s>sEw6J8dPcTF6`rcPWPn>@D0(%I6F zN!!W)ew=OUJm4Z8qSL3(xv{HvN@6AwiH|GcFquq#;>J!t)3=V0ebZdKSe)j_ww$pp~ju#$xTb%CseAi>m zV?&-^BfAhamo$*n@DL#?H(&P=H76#3q(;6egkv++j-%eFNihNPD_SS29YeiQvtkCA z6Nu>{0x>;AAf|^1#PkrpPqhCfHo>)#gGM%2srj1XgDpRDuU>fX$E9c~v){JzAle|& z9_G>;<9sh>qvUA}neSwr%uYE($}PN+|e+);5pxmc`V<=p6S&_HZf(F#QY)?H?C*i5*sIG z6~+8;YPE4a^HO346Ce*!>X{<4PiyUFF-+#7_E%bKS3Q1gY^;1`)R5NtKUZR>oI*I1 zn{GvlQQYkFEtD86tCNBvOkIF0mji!xqv+aLSrSXNBa6!=U z@ILZAAw;g3`+Q?`f{ju43jyzKI=-m5o@g6dlzln=7-laZDI7L!`adFOyKC!&`hF9# tOY~!jFrAtEaa4WZzbXt$>TqfG|8GQ!>@JIpp4tEa002ovPDHLkV1iacNxT36 diff --git a/docs/_static/logo_without_text_hires.png b/docs/_static/logo_without_text_hires.png deleted file mode 100644 index 7122304e0acf3885b9216d5f3ecb989bdbc982fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4158 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 IkzX$S6JBB{asU7T diff --git a/docs/conf.py b/docs/conf.py index b257550..37efe80 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -117,12 +117,8 @@ }, ], "use_edit_page_button": True, - "logo": { - "text": "tensortrax", - "image_light": "logo_without_text.png", - "image_dark": "logo_without_text.png", - }, } + html_context = { "github_user": "adtzlr", "github_repo": "tensortrax", diff --git a/docs/index.rst b/docs/index.rst index c8cce70..62ebd68 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,29 +1,16 @@ -.. figure:: _static/logo.png - :align: center +tensortrax documentation +======================== - Differentiable Tensors based on NumPy Arrays. - -Documentation -============= - -.. admonition:: Highlights - - - Write differentiable code with Tensors based on NumPy arrays - - Efficient evaluation of batches by elementwise-operating trailing axes - - Essential vector/tensor Hyper-Dual number math, including limited support for ``einsum`` (restricted to max. three operands) - - Math is limited but similar to NumPy, try to use ``import tensortrax.math as tm`` instead of ``import numpy as np`` inside functions to be differentiated - - Forward Mode Automatic Differentiation (AD) using Hyper-Dual Tensors, up to second order derivatives - - Create functions in terms of Hyper-Dual Tensors - - Evaluate the function, the gradient (jacobian) and the hessian of scalar-valued functions or functionals on given input arrays - - Straight-forward definition of custom functions in variational-calculus notation - - Stable gradient and hessian of eigenvalues obtained from ``eigvalsh`` in case of repeated equal eigenvalues - -Motivation +Highlights ---------- -Gradient and hessian evaluations of functions or functionals based on tensor-valued input arguments are a fundamental repetitive and (error-prone) task in constitutive hyperelastic material formulations used in continuum mechanics of solid bodies. In the worst case, conceptual ideas are impossible to pursue because the required tensorial derivatives are not readily achievable. The Hyper-Dual number approach enables a generalized and systematic way to overcome this deficiency [2]_. Compared to existing Hyper-Dual Number libaries ([3]_, [4]_) which introduce a new (hyper-) dual ``dtype`` (treated as ``dtype=object`` in NumPy), ``tensortrax`` relies on its own ``Tensor`` class. This approach involves a re-definition of all essential math operations (and NumPy-functions), whereas the ``dtype``-approach supports most basic math operations out of the box. However, in ``tensortrax``, NumPy and all its underlying linear algebra functions operate on default data types (e.g. ``dtype=float``). This allows to support functions like ``np.einsum()``. Beside the differences concerning the underlying ``dtype``, ``tensortrax`` is formulated on easy-to-understand (tensorial) calculus of variation. Hence, gradient- and hessian-vector products are evaluated with very little overhead compared to analytic formulations. - -.. important:: - Please keep in mind that ``tensortrax`` is not imitating a 100% full-featured NumPy, e.g. like https://github.com/HIPS/autograd [1]_. No arbitrary-order gradients or gradients-of-gradients are supported. The capability is limited to first- and second order gradients of a given function. Also, ``tensortrax`` provides no support for ``dtype=complex`` and ``out``-keywords are not supported. +- Designed to operate on input arrays with (elementwise-operating) trailing axes +- Essential vector/tensor Hyper-Dual number math, including limited support for ``einsum`` (restricted to max. three operands) +- Math is limited but similar to NumPy, try to use ``import tensortrax.math as tm`` instead of ``import numpy as np`` inside functions to be differentiated +- Forward Mode Automatic Differentiation (AD) using Hyper-Dual Tensors, up to second order derivatives +- Create functions in terms of Hyper-Dual Tensors +- Evaluate the function, the gradient (jacobian) and the hessian of scalar-valued functions or functionals on given input arrays +- Straight-forward definition of custom functions in variational-calculus notation +- Stable gradient and hessian of eigenvalues obtained from ``eigvalsh`` in case of repeated equal eigenvalues Installation ------------ @@ -62,7 +49,6 @@ To install optional dependencies as well, add ``[all]`` to the install command: :caption: Contents: examples/index - knowledge tensortrax License @@ -76,13 +62,6 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY You should have received a copy of the GNU General Public License along with this program. If not, see ``_. -References ----------- -.. [1] D. Maclaurin, D. Duvenaud, M. Johnson and J. Townsend, *Autograd*. Online. Available: https://github.com/HIPS/autograd. -.. [2] J. Fike and J. Alonso, *The Development of Hyper-Dual Numbers for Exact Second-Derivative Calculations*, 49th AIAA Aerospace Sciences Meeting including the New Horizons Forum and Aerospace Exposition. American Institute of Aeronautics and Astronautics, Jan. 04, 2011, doi: `10.2514/6.2011-886 `_. -.. [3] P. Rehner and G. Bauer, *Application of Generalized (Hyper-) Dual Numbers in Equation of State Modeling*, Frontiers in Chemical Engineering, vol. 3, 2021. Available: https://github.com/itt-ustutt/num-dual. -.. [4] T. Oberbichler, *HyperJet*. Online. Available: http://github.com/oberbichler/HyperJet. - Indices and tables ================== diff --git a/docs/knowledge.rst b/docs/knowledge.rst deleted file mode 100644 index f011721..0000000 --- a/docs/knowledge.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. _knowledge: - -Knowledge -========= - -.. toctree:: - :maxdepth: 1 - :caption: Modules: - - knowledge/derivative - knowledge/tensor diff --git a/docs/knowledge/derivative.rst b/docs/knowledge/derivative.rst deleted file mode 100644 index 286bee3..0000000 --- a/docs/knowledge/derivative.rst +++ /dev/null @@ -1,75 +0,0 @@ -.. _knowledge-derivative: - -Variational Calculus -==================== -The calculus of variation deals with variations, i.e. small changes in functions and functionals. A small-change in a function is evaluated by applying small changes on the input arguments, see Eq. :eq:`variation`. While the original function only depends on the input argument(s), its **variation** also depends on the **variation of the input argument(s)**. - -.. math:: - :label: variation - - f &= f(x) - - \delta f &= \delta f(x, \delta x) - - \delta f &= \frac{\partial f(x)}{\partial x}\ \delta x - -The partial derivative of a function :math:`f(x)` w.r.t. its input argument :math:`x` is obtained from Eq. :eq:`variation` by setting the variation of the input argument to :math:`\delta x=1`. The same holds also for nested functions by the application of the chain rule, see Eq. :eq:`variation-chainrule-1`. - -.. math:: - :label: variation-chainrule-1 - - y &= y(x) - - f &= f(y(x)) - - \delta f &= \delta f(y(x), \delta y(x, \delta x)) - -The partial derivative of a nested function :math:`f(y(x))` w.r.t. its base input argument :math:`x` is obtained from Eq. :eq:`variation-chainrule-2` by setting the variation of the base input argument as before to :math:`\delta x=1`. - -.. math:: - :label: variation-chainrule-2 - - \delta f &= \frac{\partial f(y)}{\partial y}\ \delta y - - \delta f &= \frac{\partial f(y)}{\partial y}\ \frac{\partial y(x)}{\partial x}\ \delta x - -By inserting given values for :math:`\delta x` one obtains the so-called **gradient-vector-product** for vector-valued input arguments. - -Example -~~~~~~~ -Given a differentiable (nested) function along with its derivative. The variation of the nested function w.r.t. its base input argument is carried out by the chain rule, see Eq. :eq:`example-variation-1` and Eq. :eq:`example-variation-2`. - -.. math:: - :label: example-variation-1 - - y(x) &= x^2 - - \delta y(x, \delta x) &= 2 x\ \delta x - -.. math:: - :label: example-variation-2 - - f(y) &= \sin(y) - - \delta f(y, \delta y) &= \cos(y)\ \delta y - -The default evaluation graph of the nested function is shown in Eq. -:eq:`evaluation-graph`. - -.. math:: - :label: evaluation-graph - - \begin{matrix} - x & & \rightarrow & & y(x) = x^2 & & \rightarrow & & f(y) = \sin(y) - \end{matrix} - -By augmenting the evaluation graph with its dual counterpart, variations are computed -side-by-side with the default graph, see Eq. :eq:`evaluation-graph-augmented`. - -.. math:: - :label: evaluation-graph-augmented - - \begin{matrix} - x & & \rightarrow & & y(x) = x^2 & & \rightarrow & & f(y) = \sin(y) \\ - \delta x & & \rightarrow & & \delta y(x, \delta x) = 2x\ \delta x & & \rightarrow & & \delta f(y, \delta y) = \cos(y)\ \delta y - \end{matrix} diff --git a/docs/knowledge/tensor.rst b/docs/knowledge/tensor.rst deleted file mode 100644 index 098ebaa..0000000 --- a/docs/knowledge/tensor.rst +++ /dev/null @@ -1,55 +0,0 @@ -.. _knowledge-tensor: - -Variations of Tensors -===================== -As an extension to the scalar-valued explanations given in :ref:`knowledge-derivative`, let us consider a scalar-valued function of as second-order tensor-based input argument, see Eq. :eq:`variation-tensor`. - -.. math:: - :label: variation-tensor - - \psi &= \psi(\boldsymbol{F}) - - \delta \psi &= \delta \psi(\boldsymbol{F}, \delta \boldsymbol{F}) - - -Let's take the trace of a tensor product as an example. The variation is carried out in Eq. :eq:`variation-tensor-example-1`. - -.. math:: - :label: variation-tensor-example-1 - - \psi &= tr(\boldsymbol{F}^T \boldsymbol{F}) = \boldsymbol{F} : \boldsymbol{F} - - \delta \psi &= \delta \boldsymbol{F} : \boldsymbol{F} + \boldsymbol{F} : \delta \boldsymbol{F} = 2 \ \boldsymbol{F} : \delta \boldsymbol{F} - -The :math:`P_{ij}` - component of the jacobian :math:`\boldsymbol{P}` is now numerically evaluated by setting the respective variational component :math:`\delta F_{ij}` of the tensor to one and all other components to zero. In total, :math:`i \cdot j` function calls are necessary to assemble the full jacobian. For example, the :math:`12` - component is evaluated as given in Eq. :eq:`variation-tensor-component`. - -.. math:: - :label: variation-tensor-component - - \delta_{(12)} \psi = \frac{\partial \psi}{\partial F_{12}} &= 2 \ \boldsymbol{F} : \delta \boldsymbol{F}_{(12)} = 2 \ \boldsymbol{F} : \begin{bmatrix} 0 & 1 & 0 \\ 0 & 0 & 0 \\ 0 & 0 & 0 \end{bmatrix} - -The second order variation, i.e. a variation applied on another variation of a function is evaluated in the same way as a first order variation, see Eq. :eq:`variation-tensor-second`. - -.. math:: - :label: variation-tensor-second - - \Delta \delta \psi = 2 \ \delta \boldsymbol{F} : \Delta \boldsymbol{F} + 2 \ \boldsymbol{F} : \Delta \delta \boldsymbol{F} - -Once again, each component :math:`A_{ijkl}` of the fourth-order hessian is numerically evaluated. In total, :math:`i \cdot j \cdot k \cdot l` function calls are necessary to assemble the full hessian (without considering symmetry). For example, the :math:`1223` - component is evaluated by setting :math:`\Delta \delta \boldsymbol{F} = \boldsymbol{0}` and :math:`\delta \boldsymbol{F}` as well as :math:`\Delta \boldsymbol{F}` as given in Eq. :eq:`variation-tensor-second-block` and Eq. :eq:`variation-tensor-second-components`. - -.. math:: - :label: variation-tensor-second-block - - \delta \boldsymbol{F}_{(12)} &= \begin{bmatrix} 0 & 1 & 0 \\ 0 & 0 & 0 \\ 0 & 0 & 0 \end{bmatrix} - - \Delta \boldsymbol{F}_{(23)} &= \begin{bmatrix} 0 & 0 & 0 \\ 0 & 0 & 1 \\ 0 & 0 & 0 \end{bmatrix} - - \Delta \delta \boldsymbol{F} &= \begin{bmatrix} 0 & 0 & 0 \\ 0 & 0 & 0 \\ 0 & 0 & 0 \end{bmatrix} - -.. math:: - :label: variation-tensor-second-components - - \Delta_{(23)} \delta_{(12)} \psi &= \Delta_{(12)} \delta_{(23)} \psi = \frac{\partial^2 \psi}{\partial F_{12}\ \partial F_{23}} - - \Delta_{(23)} \delta_{(12)} \psi &= 2 \ \delta \boldsymbol{F}_{(12)} : \Delta \boldsymbol{F}_{(23)} + 2 \ \boldsymbol{F} : \Delta \delta \boldsymbol{F} - diff --git a/docs/logo/logo.png b/docs/logo/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..5eec6fba2a9e20284d35b01c7d112df0b3ad64fb GIT binary patch literal 25253 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 + + + + + + + + + + + + + + + tensorTRAX + + + + + + diff --git a/docs/tensortrax.rst b/docs/tensortrax.rst index 1887df9..71332ea 100644 --- a/docs/tensortrax.rst +++ b/docs/tensortrax.rst @@ -7,6 +7,4 @@ API Reference :maxdepth: 1 :caption: Modules: - tensortrax/tensor - tensortrax/evaluate - tensortrax/math + tensortrax/tensor \ No newline at end of file diff --git a/docs/tensortrax/evaluate.rst b/docs/tensortrax/evaluate.rst deleted file mode 100644 index 1a4bc51..0000000 --- a/docs/tensortrax/evaluate.rst +++ /dev/null @@ -1,37 +0,0 @@ -.. _api-evaluate: - -Evaluate Functions -~~~~~~~~~~~~~~~~~~ -This module provides evaluation methods for (the gradient, Hessian or Jacobian) of a -function. - -**Core** - -.. currentmodule:: tensortrax - -.. autosummary:: - - function - gradient - hessian - jacobian - gradient_vector_product - hessian_vector_product - hessian_vectors_product - - -**Detailed API Reference** - -.. autofunction:: tensortrax.function - -.. autofunction:: tensortrax.gradient - -.. autofunction:: tensortrax.hessian - -.. autofunction:: tensortrax.jacobian - -.. autofunction:: tensortrax.gradient_vector_product - -.. autofunction:: tensortrax.hessian_vector_product - -.. autofunction:: tensortrax.hessian_vectors_product diff --git a/docs/tensortrax/math.rst b/docs/tensortrax/math.rst deleted file mode 100644 index 56bc7ba..0000000 --- a/docs/tensortrax/math.rst +++ /dev/null @@ -1,60 +0,0 @@ -.. _api-math: - -Math -~~~~ -This module provides NumPy-like math functions. - -.. toctree:: - :maxdepth: 1 - :caption: Modules: - - math/linalg - math/special - -.. currentmodule:: tensortrax.math - -.. autosummary:: - - abs - array - broadcast_to - concatenate - cos - cosh - diagonal - dot - dual_to_real - einsum - exp - external - hstack - if_else - log - log10 - matmul - maximum - minimum - ravel - real_to_dual - repeat - reshape - sign - sin - sinh - split - squeeze - sqrt - stack - sum - tan - tanh - tile - trace - transpose - vstack - - -**Detailed API Reference** - -.. automodule:: tensortrax.math - :members: abs, array, broadcast_to, concatenate, cos, cosh, diagonal, dot, dual_to_real, einsum, exp, external, hstack, if_else, log, log10, matmul, maximum, minimum, ravel, real_to_dual, repeat, reshape, sign, sin, sinh, split, squeeze, sqrt, stack, sum, tan, tanh, tile, trace, transpose, vstack diff --git a/docs/tensortrax/math/linalg.rst b/docs/tensortrax/math/linalg.rst deleted file mode 100644 index 25048c6..0000000 --- a/docs/tensortrax/math/linalg.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. _api-math-linalg: - -Linear Algebra -~~~~~~~~~~~~~~ -This module provides NumPy-like linear algebra math functions. - -.. currentmodule:: tensortrax.math.linalg - -.. autosummary:: - - det - eigh - eigvalsh - expm - inv - pinv - sqrtm - -**Detailed API Reference** - -.. automodule:: tensortrax.math.linalg - :members: det, eigh, eigvalsh, expm, inv, pinv, sqrtm diff --git a/docs/tensortrax/math/special.rst b/docs/tensortrax/math/special.rst deleted file mode 100644 index 1a69a94..0000000 --- a/docs/tensortrax/math/special.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _api-math-special: - -Special -~~~~~~~ -This module provides special (linear algebra) math functions. - -.. currentmodule:: tensortrax.math.special - -.. autosummary:: - - ddot - dev - erf - from_triu_1d - from_triu_2d - sym - tresca - triu_1d - try_stack - von_mises - -**Detailed API Reference** - -.. automodule:: tensortrax.math.special - :members: ddot, dev, erf, from_triu_1d, from_triu_2d, sym, tresca, triu_1d, try_stack, von_mises diff --git a/examples/ex01_usage.py b/examples/ex01_usage.py index 5a7bc7e..36d08d0 100644 --- a/examples/ex01_usage.py +++ b/examples/ex01_usage.py @@ -1,25 +1,10 @@ r""" Quickstart ---------- -Let's define a scalar-valued function which operates on a :class:`~tensortrax.Tensor`. -The math module :mod:`tensortrax.math` provides some essential NumPy-like functions -including linear algebra. We take the strain energy density function of the Neo-Hookean -isotropic hyperelastic material formulation as a reference example, see Eq. -:eq:`tutorial-nh`. - -.. math:: - :label: tutorial-nh - - C &= \boldsymbol{F}^T \boldsymbol{F} - - I_1 &= \text{tr} (\boldsymbol{C}) - - J &= \det (\boldsymbol{F}) - - \psi(\boldsymbol{F}) &= \frac{\mu}{2} \left( J^{-2/3}\ I_1 - 3 \right) - +Let's define a scalar-valued function which operates on a tensor. The math module +:mod:`tensortrax.math` provides some essential NumPy-like functions including linear +algebra. """ - import tensortrax as tr import tensortrax.math as tm @@ -98,7 +83,7 @@ def W(F, p, J): # %% # In a similar way, the gradient may be obtained by initiating a Tensor with the -# gradient instead of the hessian argument. +# gradient argument. # init Tensors to be used with first partial derivatives F.init(gradient=True, δx=False) diff --git a/examples/ex02_numeric_variation.py b/examples/ex02_numeric_variation.py deleted file mode 100644 index 4b530ac..0000000 --- a/examples/ex02_numeric_variation.py +++ /dev/null @@ -1,62 +0,0 @@ -r""" -Numeric calculus of variation ------------------------------ -Each Tensor has four attributes: the (real) tensor array and the (hyper-dual) -variational arrays. To obtain the :math:`12` - component of the gradient and the -:math:`1223` - component of the hessian, a tensor has to be created with the appropriate -small-changes of the tensor components (dual arrays). -""" -import numpy as np - -import tensortrax as tr -from tensortrax import Tensor, Δ, Δδ, f, δ -from tensortrax.math import trace - -δF_12 = np.array( - [ - [0, 1, 0], - [0, 0, 0], - [0, 0, 0], - ], - dtype=float, -) - -ΔF_23 = np.array( - [ - [0, 0, 0], - [0, 0, 1], - [0, 0, 0], - ], - dtype=float, -) - -x = np.eye(3) + np.arange(9).reshape(3, 3) / 10 -F = Tensor(x=x, δx=δF_12, Δx=ΔF_23, Δδx=None) -I1_C = trace(F.T @ F) - -# %% -# The function as well as the gradient and hessian components are accessible with -# helpers. -ψ = f(I1_C) -P_12 = δ(I1_C) -A_1223 = Δδ(I1_C) - -# %% -# To obtain full gradients and hessians of scalar-valued functions in one function call, -# ``tensortrax`` provides helpers (decorators) which handle the multiple function calls. -fun = lambda F: trace(F.T @ F) - -func = tr.function(fun)(x) -grad = tr.gradient(fun)(x) -hess = tr.hessian(fun)(x) - -# %% -# For tensor-valued functions, use ``jacobian()`` instead of ``gradient()``. -fun = lambda F: F.T @ F -jac = tr.jacobian(fun)(x) - -# %% -# Evaluate the gradient- as well as the hessian-vector(s)-product. -gvp = tr.gradient_vector_product(fun)(x, δx=x) -hvp = tr.hessian_vector_product(fun)(x, δx=x) -hvsp = tr.hessian_vectors_product(fun)(x, δx=x, Δx=x) diff --git a/examples/ex03_custom_extension.py b/examples/ex03_custom_extension.py deleted file mode 100644 index 4912d2f..0000000 --- a/examples/ex03_custom_extension.py +++ /dev/null @@ -1,33 +0,0 @@ -r""" -Custom Extensions ------------------ -Custom functions (extensions) are easy to implement in `tensortrax`. Beside the function -expression, three additional (dual) variation expressions have to be defined. -""" -import numpy as np - -from tensortrax import Tensor, Δ, Δδ, f, δ - - -def sin(A): - return Tensor( - x=np.sin(f(A)), - δx=np.cos(f(A)) * δ(A), - Δx=np.cos(f(A)) * Δ(A), - Δδx=-np.sin(f(A)) * δ(A) * Δ(A) + np.cos(f(A)) * Δδ(A), - ntrax=A.ntrax, - ) - - -x = np.eye(3) -y = sin(Tensor(x)) - -# %% -# .. note:: -# Contrary to NumPy's ``w, v = np.linalg.eigh(C)``, which returns eigenvalues and -# -vectors, the differentiable ``w, M = tm.linalg.eigh(C)`` function returns -# eigenvalues and eigenbases of symmetric real-valued tensors. -# -# .. tip:: -# Feel free to `contribute `_ missing -# math-functions to `src/tensortrax/math/_math_tensor.py `_ 📃 ✏️. diff --git a/pyproject.toml b/pyproject.toml index 01951ec..3753bef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ profile = "black" [project] name = "tensortrax" -description = "Differentiable Tensors based on NumPy Arrays" +description = "Math on (Hyper-Dual) Tensors with Trailing Axes" readme = "README.md" authors = [ {name = "Andreas Dutzler"}, diff --git a/src/tensortrax/__about__.py b/src/tensortrax/__about__.py index 62db274..c6ee375 100644 --- a/src/tensortrax/__about__.py +++ b/src/tensortrax/__about__.py @@ -2,4 +2,4 @@ tensorTRAX: Math on (Hyper-Dual) Tensors with Trailing Axes. """ -__version__ = "0.25.3" +__version__ = "0.26.0" diff --git a/src/tensortrax/_evaluate.py b/src/tensortrax/_evaluate.py index 56697a4..6a48882 100644 --- a/src/tensortrax/_evaluate.py +++ b/src/tensortrax/_evaluate.py @@ -143,50 +143,7 @@ def concat(arrays, axis): def function(fun, wrt=0, ntrax=0, parallel=False): - r"""Evaluate a function. - - Parameters - ---------- - fun : callable - The function to be evaluated. - wrt : int or str, optional - The input argument which will be treated as :class:`~tensortrax.Tensor` (default - is 0). - ntrax : int, optional - Number of elementwise-operating trailing axes (batch dimensions). Default is 0. - parallel : bool, optional - Flag to evaluate the function in parallel (threaded). - - Returns - ------- - ndarray - NumPy array containing the function result. - - Examples - -------- - - >>> import numpy as np - >>> import tensortrax as tr - >>> import tensortrax.math as tm - >>> - >>> def fun(F, mu=1): - >>> C = F.T @ F - >>> I1 = tm.trace(C) - >>> J = tm.linalg.det(F) - >>> return mu / 2 * (J ** (-2 / 3) * I1 - 3) - >>> - >>> np.random.seed(125161) - >>> F = (np.eye(3) + np.random.rand(20, 8, 3, 3) / 10).T - >>> - >>> F.shape - (3, 3, 8, 20) - - >>> W = tr.function(fun, wrt=0, ntrax=2)(F) - >>> W = tr.function(fun, wrt="F", ntrax=2)(F=F) - >>> - >>> W.shape - >>> (8, 20) - """ + "Evaluate a function." @wraps(fun) def evaluate_function(*args, **kwargs): @@ -213,57 +170,7 @@ def kernel(args, kwargs): def gradient(fun, wrt=0, ntrax=0, parallel=False, full_output=False, sym=False): - r"""Evaluate the gradient of a scalar-valued function. - - Parameters - ---------- - fun : callable - The function to be evaluated. - wrt : int or str, optional - The input argument which will be treated as :class:`~tensortrax.Tensor` (default - is 0). The gradient is carried out with respect to this argument. - ntrax : int, optional - Number of elementwise-operating trailing axes (batch dimensions). Default is 0. - parallel : bool, optional - Flag to evaluate the gradient in parallel (threaded). - full_output: bool, optional - Return the gradient and the function (default is False). - sym : bool, optional - Apply the variations only on the upper-triangle entries of a symmetric second - order tensor. This is a performance feature and requires no modification of the - callable ``fun`` and the input arguments, including ``wrt``. Default is False. - - Returns - ------- - ndarray or list of ndarray - NumPy array containing the gradient result. If ``full_output=True``, the - function is also returned. - - Examples - -------- - - >>> import numpy as np - >>> import tensortrax as tr - >>> import tensortrax.math as tm - >>> - >>> def fun(F, mu=1): - >>> C = F.T @ F - >>> I1 = tm.trace(C) - >>> J = tm.linalg.det(F) - >>> return mu / 2 * (J ** (-2 / 3) * I1 - 3) - >>> - >>> np.random.seed(125161) - >>> F = (np.eye(3) + np.random.rand(20, 8, 3, 3) / 10).T - >>> - >>> F.shape - (3, 3, 8, 20) - - >>> dWdF = tr.gradient(fun, wrt=0, ntrax=2)(F) - >>> dWdF = tr.gradient(fun, wrt="F", ntrax=2)(F=F) - >>> - >>> dWdF.shape - >>> (3, 3, 8, 20) - """ + "Evaluate the gradient of a scalar-valued function." @wraps(fun) def evaluate_gradient(*args, **kwargs): @@ -295,57 +202,7 @@ def kernel(args, kwargs): def hessian(fun, wrt=0, ntrax=0, parallel=False, full_output=False, sym=False): - r"""Evaluate the Hessian of a scalar-valued function. - - Parameters - ---------- - fun : callable - The function to be evaluated. - wrt : int or str, optional - The input argument which will be treated as :class:`~tensortrax.Tensor` (default - is 0). The Hessian is carried out with respect to this argument. - ntrax : int, optional - Number of elementwise-operating trailing axes (batch dimensions). Default is 0. - parallel : bool, optional - Flag to evaluate the Hessian in parallel (threaded). - full_output: bool, optional - Return the hessian, the gradient and the function (default is False). - sym : bool, optional - Apply the variations only on the upper-triangle entries of a symmetric second - order tensor. This is a performance feature and requires no modification of the - callable ``fun`` and the input arguments, including ``wrt``. Default is False. - - Returns - ------- - ndarray or list of ndarray - NumPy array containing the Hessian result. If ``full_output=True``, the - gradient and the function are also returned. - - Examples - -------- - - >>> import numpy as np - >>> import tensortrax as tr - >>> import tensortrax.math as tm - >>> - >>> def fun(F, mu=1): - >>> C = F.T @ F - >>> I1 = tm.trace(C) - >>> J = tm.linalg.det(F) - >>> return mu / 2 * (J ** (-2 / 3) * I1 - 3) - >>> - >>> np.random.seed(125161) - >>> F = (np.eye(3) + np.random.rand(20, 8, 3, 3) / 10).T - >>> - >>> F.shape - (3, 3, 8, 20) - - >>> d2WdFdF = tr.hessian(fun, wrt=0, ntrax=2)(F) - >>> d2WdFdF = tr.hessian(fun, wrt="F", ntrax=2)(F=F) - >>> - >>> d2WdFdF.shape - >>> (3, 3, 3, 3, 8, 20) - """ + "Evaluate the hessian of a scalar-valued function." @wraps(fun) def evaluate_hessian(*args, **kwargs): @@ -385,51 +242,7 @@ def kernel(args, kwargs): def jacobian(fun, wrt=0, ntrax=0, parallel=False, full_output=False): - r"""Evaluate the Jacobian of a tensor-valued function. - - Parameters - ---------- - fun : callable - The function to be evaluated. - wrt : int or str, optional - The input argument which will be treated as :class:`~tensortrax.Tensor` (default - is 0). The Jacobian is carried out with respect to this argument. - ntrax : int, optional - Number of elementwise-operating trailing axes (batch dimensions). Default is 0. - parallel : bool, optional - Flag to evaluate the Jacobian in parallel (threaded). - full_output: bool, optional - Return the Jacobian and the function (default is False). - - Returns - ------- - ndarray - NumPy array containing the Jacobian result. - - Examples - -------- - - >>> import numpy as np - >>> import tensortrax as tr - >>> import tensortrax.math as tm - >>> - >>> def fun(C, mu=1): - >>> I3 = tm.linalg.det(C) - >>> return mu * tm.special.dev(I3 ** (-1 / 3) * C) @ tm.linalg.inv(C) - >>> - >>> np.random.seed(125161) - >>> F = (np.eye(3) + np.random.rand(20, 8, 3, 3) / 10).T - >>> C = np.einsum("ki...,kj...->ij...", F, F) - >>> - >>> C.shape - (3, 3, 8, 20) - - >>> dSdC = tr.jacobian(fun, wrt=0, ntrax=2)(C) - >>> dSdC = tr.jacobian(fun, wrt="C", ntrax=2)(C=C) - >>> - >>> dSdC.shape - >>> (3, 3, 3, 3, 8, 20) - """ + "Evaluate the jacobian of a function." @wraps(fun) def evaluate_jacobian(*args, **kwargs): @@ -456,60 +269,7 @@ def kernel(args, kwargs): def gradient_vector_product(fun, wrt=0, ntrax=0, parallel=False): - r"""Evaluate the gradient-vector-product of a scalar-valued function. - - Parameters - ---------- - fun : callable - The function to be evaluated. Its signature is extended to - :func:`fun(*args, δx, **kwargs)`, where the added ``δx``-argument is the vector - of the gradient-vector product. - wrt : int or str, optional - The input argument which will be treated as :class:`~tensortrax.Tensor` (default - is 0). The gradient-vector-product is carried out with respect to this argument. - ntrax : int, optional - Number of elementwise-operating trailing axes (batch dimensions). Default is 0. - parallel : bool, optional - Flag to evaluate the gradient-vector-product in parallel (threaded). - - Returns - ------- - ndarray - NumPy array containing the gradient-vector-product result. - - Notes - ----- - The *vector* :math:`\delta x` and the tensor-argument ``wrt`` must have equal or - broadcast-compatible shapes. This means that the *vector* is not restricted to be a - one-dimensional array but must be an array with compatible shape instead. - - Examples - -------- - - >>> import numpy as np - >>> import tensortrax as tr - >>> import tensortrax.math as tm - >>> - >>> def fun(F, mu=1): - >>> C = F.T @ F - >>> I1 = tm.trace(C) - >>> J = tm.linalg.det(F) - >>> return mu / 2 * (J ** (-2 / 3) * I1 - 3) - >>> - >>> np.random.seed(125161) - >>> F = (np.eye(3) + np.random.rand(20, 8, 3, 3) / 10).T - >>> F.shape - (3, 3, 8, 20) - - >>> np.random.seed(63254) - >>> δF = np.random.rand(3, 3, 8, 20) / 10 - >>> δF.shape - (3, 3, 8, 20) - - >>> dW = tr.gradient_vector_product(fun, wrt=0, ntrax=2)(F, δx=δF) - >>> dW.shape - >>> (8, 20) - """ + "Evaluate the gradient-vector-product of a function." @wraps(fun) def evaluate_gradient_vector_product(*args, δx, **kwargs): @@ -524,60 +284,7 @@ def evaluate_gradient_vector_product(*args, δx, **kwargs): def hessian_vector_product(fun, wrt=0, ntrax=0, parallel=False): - r"""Evaluate the Hessian-vector-product of a scalar-valued function. - - Parameters - ---------- - fun : callable - The function to be evaluated. Its signature is extended to - :func:`fun(*args, δx, **kwargs)`, where the added ``δx``-argument is the vector - of the Hessian-vector product. - wrt : int or str, optional - The input argument which will be treated as :class:`~tensortrax.Tensor` (default - is 0). The Hessian-vector-product is carried out with respect to this argument. - ntrax : int, optional - Number of elementwise-operating trailing axes (batch dimensions). Default is 0. - parallel : bool, optional - Flag to evaluate the gradient-vector-product in parallel (threaded). - - Returns - ------- - ndarray - NumPy array containing the Hessian-vector-product result. - - Notes - ----- - The *vector* :math:`\delta x` and the tensor-argument ``wrt`` must have equal or - broadcast-compatible shapes. This means that the *vector* is not restricted to be a - one-dimensional array but must be an array with compatible shape instead. - - Examples - -------- - - >>> import numpy as np - >>> import tensortrax as tr - >>> import tensortrax.math as tm - >>> - >>> def fun(F, mu=1): - >>> C = F.T @ F - >>> I1 = tm.trace(C) - >>> J = tm.linalg.det(F) - >>> return mu / 2 * (J ** (-2 / 3) * I1 - 3) - >>> - >>> np.random.seed(125161) - >>> F = (np.eye(3) + np.random.rand(20, 8, 3, 3) / 10).T - >>> F.shape - (3, 3, 8, 20) - - >>> np.random.seed(63254) - >>> δF = np.random.rand(3, 3, 8, 20) / 10 - >>> δF.shape - (3, 3, 8, 20) - - >>> dP = tr.hessian_vector_product(fun, wrt=0, ntrax=2)(F, δx=δF) - >>> dP.shape - >>> (3, 3, 8, 20) - """ + "Evaluate the hessian-vector-product of a function." @wraps(fun) def evaluate_hessian_vector_product(*args, δx, **kwargs): @@ -592,67 +299,7 @@ def evaluate_hessian_vector_product(*args, δx, **kwargs): def hessian_vectors_product(fun, wrt=0, ntrax=0, parallel=False): - r"""Evaluate the vector-Hessian-vector- or Hessian-vectors-product of a scalar- - valued function. - - Parameters - ---------- - fun : callable - The function to be evaluated. Its signature is extended to - :func:`fun(*args, δx, Δx, **kwargs)`, where the added ``δx``- and ``Δx``- - arguments are the vectors of the Hessian-vectors product. - wrt : int or str, optional - The input argument which will be treated as :class:`~tensortrax.Tensor` (default - is 0). The Hessian-vectors-product is carried out with respect to this argument. - ntrax : int, optional - Number of elementwise-operating trailing axes (batch dimensions). Default is 0. - parallel : bool, optional - Flag to evaluate the gradient-vector-product in parallel (threaded). - - Returns - ------- - ndarray - NumPy array containing the Hessian-vectors-product result. - - Notes - ----- - The *vectors* :math:`\delta x` and :math:`\Delta x` as well as the tensor-argument - ``wrt`` must have equal or broadcast-compatible shapes. This means that the - *vectors* are not restricted to be one-dimensional arrays but must be arrays with - compatible shapes instead. - - Examples - -------- - - >>> import numpy as np - >>> import tensortrax as tr - >>> import tensortrax.math as tm - >>> - >>> def fun(F, mu=1): - >>> C = F.T @ F - >>> I1 = tm.trace(C) - >>> J = tm.linalg.det(F) - >>> return mu / 2 * (J ** (-2 / 3) * I1 - 3) - >>> - >>> np.random.seed(125161) - >>> F = (np.eye(3) + np.random.rand(20, 8, 3, 3) / 10).T - >>> F.shape - (3, 3, 8, 20) - - >>> np.random.seed(63254) - >>> δF = np.random.rand(3, 3, 8, 20) / 10 - >>> δF.shape - (3, 3, 8, 20) - - >>> np.random.seed(85476) - >>> ΔF = np.random.rand(3, 3, 8, 20) / 10 - >>> ΔF.shape - (3, 3, 8, 20) - - >>> ΔδW = tr.hessian_vectors_product(fun, wrt=0, ntrax=2)(F, δx=δF, Δx=ΔF) - >>> ΔδW.shape - >>> (8, 20) - """ + "Evaluate the hessian-vectors-product of a function." @wraps(fun) def evaluate_hessian_vectors_product(*args, δx, Δx, **kwargs): diff --git a/src/tensortrax/_tensor.py b/src/tensortrax/_tensor.py index bbe6890..c9c7f23 100644 --- a/src/tensortrax/_tensor.py +++ b/src/tensortrax/_tensor.py @@ -449,15 +449,12 @@ def T(self): return transpose(self) def ravel(self, order="C"): - "Return a contiguous flattened array." return ravel(self, order=order) def reshape(self, *shape, order="C"): - "Gives a new shape to an array without changing its data." return reshape(self, newshape=shape, order=order) def squeeze(self, axis=None): - "Remove axes of length one." return squeeze(self, axis=axis) def dual2real(self, like): @@ -537,7 +534,6 @@ def mul(A, B): def ravel(A, order="C"): - "Return a contiguous flattened array." if isinstance(A, Tensor): δtrax = δ(A).shape[len(A.shape) :] Δtrax = Δ(A).shape[len(A.shape) :] @@ -554,7 +550,6 @@ def ravel(A, order="C"): def squeeze(A, axis=None): - "Remove axes of length one." if isinstance(A, Tensor): if axis is None: if 1 in A.shape: @@ -573,7 +568,6 @@ def squeeze(A, axis=None): def reshape(A, newshape, order="C"): - "Gives a new shape to an array without changing its data." if isinstance(A, Tensor): δtrax = δ(A).shape[len(A.shape) :] Δtrax = Δ(A).shape[len(A.shape) :] @@ -755,14 +749,12 @@ def einsum(subscripts, *operands): def transpose(A): - "Returns an array with axes transposed." ij = "abcdefghijklmnopqrstuvwxyz"[: len(A.shape)] ji = ij[::-1] return einsum(f"{ij}...->{ji}...", A) def matmul(A, B): - "Matrix product of two arrays." ik = "ik"[2 - len(A.shape) :] kj = "kj"[: len(B.shape)] ij = (ik + kj).replace("k", "") diff --git a/src/tensortrax/math/__init__.py b/src/tensortrax/math/__init__.py index 907df8b..27c71a7 100644 --- a/src/tensortrax/math/__init__.py +++ b/src/tensortrax/math/__init__.py @@ -1,3 +1,7 @@ +""" +tensorTRAX: Math on (Hyper-Dual) Tensors with Trailing Axes. +""" + from .._tensor import broadcast_to from .._tensor import dual_to_real from .._tensor import dual_to_real as dual2real diff --git a/src/tensortrax/math/_math_tensor.py b/src/tensortrax/math/_math_tensor.py index 3f22ba7..ba70b83 100644 --- a/src/tensortrax/math/_math_tensor.py +++ b/src/tensortrax/math/_math_tensor.py @@ -41,17 +41,14 @@ def array(object, dtype=None, like=None, shape=None): def trace(A): - "Return the sum along diagonals of the array." return einsum("ii...->...", A) def transpose(A): - "Returns an array with axes transposed." return einsum("ij...->ji...", A) def sum(A, axis=0): - "Sum of array elements over a given axis." if isinstance(A, Tensor): return Tensor( x=np.sum(f(A), axis=axis), @@ -65,7 +62,6 @@ def sum(A, axis=0): def sign(A): - "Returns an element-wise indication of the sign of a number." if isinstance(A, Tensor): return Tensor( x=np.sign(f(A)), @@ -79,7 +75,6 @@ def sign(A): def abs(A): - "Calculate the absolute value element-wise." if isinstance(A, Tensor): return Tensor( x=np.abs(f(A)), @@ -100,7 +95,6 @@ def sqrt(A): def sin(A): - "Trigonometric sine, element-wise." if isinstance(A, Tensor): return Tensor( x=np.sin(f(A)), @@ -114,7 +108,6 @@ def sin(A): def cos(A): - "Cosine element-wise." if isinstance(A, Tensor): return Tensor( x=np.cos(f(A)), @@ -128,7 +121,6 @@ def cos(A): def tan(A): - "Compute tangent element-wise." if isinstance(A, Tensor): return Tensor( x=np.tan(f(A)), @@ -143,7 +135,6 @@ def tan(A): def sinh(A): - "Hyperbolic sine, element-wise." if isinstance(A, Tensor): return Tensor( x=np.sinh(f(A)), @@ -157,7 +148,6 @@ def sinh(A): def cosh(A): - "Hyperbolic cosine, element-wise." if isinstance(A, Tensor): return Tensor( x=np.cosh(f(A)), @@ -171,7 +161,6 @@ def cosh(A): def tanh(A): - "Compute hyperbolic tangent element-wise." if isinstance(A, Tensor): x = np.tanh(f(A)) return Tensor( @@ -186,7 +175,6 @@ def tanh(A): def exp(A): - "Calculate the exponential of all elements in the input array." if isinstance(A, Tensor): x = np.exp(f(A)) return Tensor( @@ -201,7 +189,6 @@ def exp(A): def log(A): - "Natural logarithm, element-wise." if isinstance(A, Tensor): x = np.log(f(A)) return Tensor( @@ -216,7 +203,6 @@ def log(A): def log10(A): - "Return the base 10 logarithm of the input array, element-wise." if isinstance(A, Tensor): x = np.log10(f(A)) return Tensor( diff --git a/src/tensortrax/math/linalg/__init__.py b/src/tensortrax/math/linalg/__init__.py index d85ec5c..fe8b8f1 100644 --- a/src/tensortrax/math/linalg/__init__.py +++ b/src/tensortrax/math/linalg/__init__.py @@ -1,3 +1,7 @@ +""" +tensorTRAX: Math on (Hyper-Dual) Tensors with Trailing Axes. +""" + from ._linalg_array import det as _det from ._linalg_array import inv as _inv from ._linalg_array import pinv as _pinv diff --git a/src/tensortrax/math/special/__init__.py b/src/tensortrax/math/special/__init__.py index 7e06f7d..a9bac1b 100644 --- a/src/tensortrax/math/special/__init__.py +++ b/src/tensortrax/math/special/__init__.py @@ -1,3 +1,7 @@ +""" +tensorTRAX: Math on (Hyper-Dual) Tensors with Trailing Axes. +""" + from ._special_tensor import ( ddot, dev, diff --git a/tox.ini b/tox.ini index 688150b..902ebb9 100644 --- a/tox.ini +++ b/tox.ini @@ -3,7 +3,7 @@ envlist = py3 isolated_build = True [tool:pytest] -addopts = --doctest-modules +addopts = --cov tensortrax --cov-report xml --cov-report term [testenv] deps = @@ -11,4 +11,4 @@ deps = pytest-cov extras = all commands = - pytest tests {posargs} + pytest