diff --git a/REQUIRE b/REQUIRE index db757995..fc7174e4 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1,6 +1,7 @@ julia 0.6- StaticArrays 0.5.0 -DiffBase 0.0.3 -Calculus 0.2.0 +DiffBase 0.2.0 NaNMath 0.2.2 SpecialFunctions 0.1.0 +RealInterface 0.0.2 +CommonSubexpressions 0.0.1 diff --git a/docs/_rst/source/contributing.rst b/docs/_rst/source/contributing.rst index 7de30bdc..35b5d105 100644 --- a/docs/_rst/source/contributing.rst +++ b/docs/_rst/source/contributing.rst @@ -12,68 +12,26 @@ If you're new GitHub, here's an outline of the workflow you should use: 3. Apply your code changes to the branch on your fork 4. When you're done, submit a PR to ForwardDiff to merge your fork into ForwardDiff's master branch. -Manually Optimizing Unary Functions ------------------------------------ +Adding New Derivative Definitions +--------------------------------- -To see a list of functions to pick from, look at ``ForwardDiff.AUTO_DEFINED_UNARY_FUNCS``: +In general, new derivative implementations for ``Dual`` are automatically defined via +simple symbolic rules. ForwardDiff accomplishes this by looping over the `the function names +listed in the RealInterface package`_, and for every function (and relevant arity), it +attempts to generate a ``Dual`` definition by applying the `symbolic rules provided by the +DiffBase package`_. Conveniently, these auto-generated definitions are also automatically +tested. -.. code-block:: julia +Thus, in order to add a new derivative implementation for ``Dual``, you should do the +following: - julia> import ForwardDiff +1. Make sure the name of the function is appropriately listed in the RealInterface package +2. Define the appropriate derivative rule(s) in DiffBase +3. Check that calling the function on ``Dual`` instances delivers the desired result. - julia> ForwardDiff.AUTO_DEFINED_UNARY_FUNCS - 57-element Array{Symbol,1}: - :sqrt - :cbrt - :abs2 - :inv - :log - :log10 - :log2 - :log1p - :exp - :exp2 - :expm1 - :sin - :cos - :tan - ⋮ +Depending on the arity of your function and its category in RealInterface, +ForwardDiff's auto-definition mechanism might need to be expanded to include it. +If this is the case, ForwardDiff's maintainers can help you out. -Some of these functions may have already been manually optimized. To see what functions have -already been done, go to ``src/dual.jl`` and scroll down to the ``Special Cases`` section. - -The functions in ``ForwardDiff.AUTO_DEFINED_UNARY_FUNCS`` are automatically tested as part -of ForwardDiff's test suite, so you don't need to write tests yourself. You can test your -changes by running ``Pkg.test("ForwardDiff")``. - -If everything passes, you can submit a PR to the ForwardDiff repository to share your work! - -Implementing New Functions --------------------------- - -Unary Functions Via Calculus.jl -+++++++++++++++++++++++++++++++ - -The easiest way to add support for a new function is actually to define a derivative rule -for the function in Calculus's `symbolic differentiation code`_, which ForwardDiff then uses -to generate the function's definition on ``Dual`` numbers. To accomplish this: - -1. Open an issue in ForwardDiff with the title "Supporting f(x)" (obviously replacing "f(x)"" with the function you wish to support). -2. Open a PR to Calculus that adds the relevant differentiation rule(s) and tests. In the PR's description, be sure to mention the relevant ForwardDiff issue such that GitHub links the two. -3. Once the PR to Calculus is accepted, we can check to make sure that the function works appropriately in ForwardDiff. If it does, then you're done, and the issue in ForwardDiff can be considered resolved! - -.. _`symbolic differentiation code`: https://github.com/johnmyleswhite/Calculus.jl/blob/master/src/differentiate.jl#L115 - -Manually Adding Functions to ForwardDiff -++++++++++++++++++++++++++++++++++++++++ - -Some functions aren't suitable for auto-definition via the Calculus package. An example of -such a function is the non-unary function ``atan2``, which is defined manually in -``ForwardDiff/src/dual.jl``. - -The process for manually adding functions to ForwardDiff without going through Calculus.jl -is essentially the same as the process for manually optimizing existing functions, with the -additional requirement that you'll have to write the tests yourself. New tests for ``Dual`` -numbers can be placed in `DualTest.jl`_. - -.. _`DualTest.jl`: https://github.com/JuliaDiff/ForwardDiff.jl/tree/master/test/DualTest.jl +.. _`the function names listed in the RealInterface package`: https://github.com/jrevels/RealInterface.jl +.. _`symbolic rules provided by the DiffBase package`: https://github.com/JuliaDiff/DiffBase.jl/blob/master/src/rules.jl diff --git a/docs/_sources/contributing.txt b/docs/_sources/contributing.txt index 7de30bdc..35b5d105 100644 --- a/docs/_sources/contributing.txt +++ b/docs/_sources/contributing.txt @@ -12,68 +12,26 @@ If you're new GitHub, here's an outline of the workflow you should use: 3. Apply your code changes to the branch on your fork 4. When you're done, submit a PR to ForwardDiff to merge your fork into ForwardDiff's master branch. -Manually Optimizing Unary Functions ------------------------------------ +Adding New Derivative Definitions +--------------------------------- -To see a list of functions to pick from, look at ``ForwardDiff.AUTO_DEFINED_UNARY_FUNCS``: +In general, new derivative implementations for ``Dual`` are automatically defined via +simple symbolic rules. ForwardDiff accomplishes this by looping over the `the function names +listed in the RealInterface package`_, and for every function (and relevant arity), it +attempts to generate a ``Dual`` definition by applying the `symbolic rules provided by the +DiffBase package`_. Conveniently, these auto-generated definitions are also automatically +tested. -.. code-block:: julia +Thus, in order to add a new derivative implementation for ``Dual``, you should do the +following: - julia> import ForwardDiff +1. Make sure the name of the function is appropriately listed in the RealInterface package +2. Define the appropriate derivative rule(s) in DiffBase +3. Check that calling the function on ``Dual`` instances delivers the desired result. - julia> ForwardDiff.AUTO_DEFINED_UNARY_FUNCS - 57-element Array{Symbol,1}: - :sqrt - :cbrt - :abs2 - :inv - :log - :log10 - :log2 - :log1p - :exp - :exp2 - :expm1 - :sin - :cos - :tan - ⋮ +Depending on the arity of your function and its category in RealInterface, +ForwardDiff's auto-definition mechanism might need to be expanded to include it. +If this is the case, ForwardDiff's maintainers can help you out. -Some of these functions may have already been manually optimized. To see what functions have -already been done, go to ``src/dual.jl`` and scroll down to the ``Special Cases`` section. - -The functions in ``ForwardDiff.AUTO_DEFINED_UNARY_FUNCS`` are automatically tested as part -of ForwardDiff's test suite, so you don't need to write tests yourself. You can test your -changes by running ``Pkg.test("ForwardDiff")``. - -If everything passes, you can submit a PR to the ForwardDiff repository to share your work! - -Implementing New Functions --------------------------- - -Unary Functions Via Calculus.jl -+++++++++++++++++++++++++++++++ - -The easiest way to add support for a new function is actually to define a derivative rule -for the function in Calculus's `symbolic differentiation code`_, which ForwardDiff then uses -to generate the function's definition on ``Dual`` numbers. To accomplish this: - -1. Open an issue in ForwardDiff with the title "Supporting f(x)" (obviously replacing "f(x)"" with the function you wish to support). -2. Open a PR to Calculus that adds the relevant differentiation rule(s) and tests. In the PR's description, be sure to mention the relevant ForwardDiff issue such that GitHub links the two. -3. Once the PR to Calculus is accepted, we can check to make sure that the function works appropriately in ForwardDiff. If it does, then you're done, and the issue in ForwardDiff can be considered resolved! - -.. _`symbolic differentiation code`: https://github.com/johnmyleswhite/Calculus.jl/blob/master/src/differentiate.jl#L115 - -Manually Adding Functions to ForwardDiff -++++++++++++++++++++++++++++++++++++++++ - -Some functions aren't suitable for auto-definition via the Calculus package. An example of -such a function is the non-unary function ``atan2``, which is defined manually in -``ForwardDiff/src/dual.jl``. - -The process for manually adding functions to ForwardDiff without going through Calculus.jl -is essentially the same as the process for manually optimizing existing functions, with the -additional requirement that you'll have to write the tests yourself. New tests for ``Dual`` -numbers can be placed in `DualTest.jl`_. - -.. _`DualTest.jl`: https://github.com/JuliaDiff/ForwardDiff.jl/tree/master/test/DualTest.jl +.. _`the function names listed in the RealInterface package`: https://github.com/jrevels/RealInterface.jl +.. _`symbolic rules provided by the DiffBase package`: https://github.com/JuliaDiff/DiffBase.jl/blob/master/src/rules.jl diff --git a/docs/contributing.html b/docs/contributing.html index 5a1973b7..9ed2601b 100644 --- a/docs/contributing.html +++ b/docs/contributing.html @@ -92,12 +92,7 @@ @@ -156,60 +151,24 @@

How to Contribute -

Manually Optimizing Unary Functions

-

To see a list of functions to pick from, look at ForwardDiff.AUTO_DEFINED_UNARY_FUNCS:

-
julia> import ForwardDiff
-
-julia> ForwardDiff.AUTO_DEFINED_UNARY_FUNCS
-57-element Array{Symbol,1}:
- :sqrt
- :cbrt
- :abs2
- :inv
- :log
- :log10
- :log2
- :log1p
- :exp
- :exp2
- :expm1
- :sin
- :cos
- :tan
- 
-
-
-

Some of these functions may have already been manually optimized. To see what functions have -already been done, go to src/dual.jl and scroll down to the Special Cases section.

-

The functions in ForwardDiff.AUTO_DEFINED_UNARY_FUNCS are automatically tested as part -of ForwardDiff’s test suite, so you don’t need to write tests yourself. You can test your -changes by running Pkg.test("ForwardDiff").

-

If everything passes, you can submit a PR to the ForwardDiff repository to share your work!

- -
-

Implementing New Functions

-
-

Unary Functions Via Calculus.jl

-

The easiest way to add support for a new function is actually to define a derivative rule -for the function in Calculus’s symbolic differentiation code, which ForwardDiff then uses -to generate the function’s definition on Dual numbers. To accomplish this:

+
+

Adding New Derivative Definitions

+

In general, new derivative implementations for Dual are automatically defined via +simple symbolic rules. ForwardDiff accomplishes this by looping over the the function names +listed in the RealInterface package, and for every function (and relevant arity), it +attempts to generate a Dual definition by applying the symbolic rules provided by the +DiffBase package. Conveniently, these auto-generated definitions are also automatically +tested.

+

Thus, in order to add a new derivative implementation for Dual, you should do the +following:

    -
  1. Open an issue in ForwardDiff with the title “Supporting f(x)” (obviously replacing “f(x)”” with the function you wish to support).
  2. -
  3. Open a PR to Calculus that adds the relevant differentiation rule(s) and tests. In the PR’s description, be sure to mention the relevant ForwardDiff issue such that GitHub links the two.
  4. -
  5. Once the PR to Calculus is accepted, we can check to make sure that the function works appropriately in ForwardDiff. If it does, then you’re done, and the issue in ForwardDiff can be considered resolved!
  6. +
  7. Make sure the name of the function is appropriately listed in the RealInterface package
  8. +
  9. Define the appropriate derivative rule(s) in DiffBase
  10. +
  11. Check that calling the function on Dual instances delivers the desired result.
-
-
-

Manually Adding Functions to ForwardDiff

-

Some functions aren’t suitable for auto-definition via the Calculus package. An example of -such a function is the non-unary function atan2, which is defined manually in -ForwardDiff/src/dual.jl.

-

The process for manually adding functions to ForwardDiff without going through Calculus.jl -is essentially the same as the process for manually optimizing existing functions, with the -additional requirement that you’ll have to write the tests yourself. New tests for Dual -numbers can be placed in DualTest.jl.

-
+

Depending on the arity of your function and its category in RealInterface, +ForwardDiff’s auto-definition mechanism might need to be expanded to include it. +If this is the case, ForwardDiff’s maintainers can help you out.

diff --git a/docs/index.html b/docs/index.html index 2adec17e..494015ba 100644 --- a/docs/index.html +++ b/docs/index.html @@ -183,8 +183,7 @@

ForwardDiff.jlHow to Contribute diff --git a/docs/searchindex.js b/docs/searchindex.js index 9313df25..3f441d03 100644 --- a/docs/searchindex.js +++ b/docs/searchindex.js @@ -1 +1 @@ -Search.setIndex({envversion:49,filenames:["advanced_usage","basic_api","contributing","how_it_works","index","install","limitations","upgrade"],objects:{ForwardDiff:{GradientConfig:[1,0,1,""],HessianConfig:[1,0,1,""],JacobianConfig:[1,0,1,""],derivative:[1,0,1,""],gradient:[1,0,1,""],hessian:[1,0,1,""],jacobian:[1,0,1,""]}},objnames:{"0":["py","function","Python function"]},objtypes:{"0":"py:function"},terms:{"abstract":3,"break":6,"byte":0,"case":[0,1,2,3],"catch":1,"default":[0,1,7],"export":7,"final":0,"import":2,"long":6,"true":[0,7],"try":0,"void":0,"while":[0,1,4],_70842:0,_70852:0,abil:1,abl:0,about:[3,4,7],abov:[3,7],abs2:2,abstractarrai:[1,6],abstractconfig:0,abstractvector:3,accept:[2,6],accomplish:[0,2],accuraci:4,actual:[0,2],add:[0,2,5],addit:[0,2,3],advantag:4,advis:1,affect:0,after:2,algorithm:4,align:0,all:[0,1,2,6,7],alloc:[0,1],allow:[0,1,3],allresult:7,almost:0,alreadi:2,also:[0,1,6],amen:0,ani:[1,4,6],answer:[0,7],api:0,apply:2,appropri:[0,2],aren:[0,2],arg:[0,1],argument:[1,6],arithmet:0,around:2,arrai:[0,2,6],articl:4,arxiv:4,assert:0,assign:6,assum:[1,3],atan2:2,attempt:0,author:[0,4],auto:2,auto_defined_unary_funcs:2,automat:[0,1,2,3,4,7],avoid:[6,7],awai:3,ax_mul_bx:6,bandwidth:0,base:[0,1,3],basic:0,been:2,befor:0,behavior:[0,3],behvaior:0,below:1,benchmark:0,best:0,better:0,between:7,binari:0,bitcast:0,bitcod:0,blas:6,block:0,both:[3,4],branch:2,brief:2,bring:0,buffer:[0,1],build:0,built:0,bundl:1,calcul:[0,3],call:[0,1,3,6],callabl:4,can:[0,1,2,3,4,6,7],cannot:[1,6],cast:0,caus:0,cbrt:2,central:3,certain:0,cfg10:0,cfg1:0,cfg4:0,cfg:[0,1],chain:3,chang:[2,7],check:[0,2],chunk_siz:7,cite:4,clearer:7,code:[0,2,3,6,7],code_llvm:0,collect:6,comment:6,common:4,compil:0,complet:0,compon:[0,3],compos:[0,3,6,7],comput:[0,1],conflict:7,confus:[1,3],conjunct:0,consid:2,constant:0,construct:[0,1],constructor:[0,1],consult:0,contain:[1,3],conveni:1,copi:0,correct:0,cost:0,could:0,coupl:3,cours:0,creat:4,cross:3,cumprod:0,current:[5,7],data:0,decreas:[0,1],defin:[0,2,7],definit:2,depend:[0,4],deriv:0,derivativeconfig:1,describ:0,descript:2,detail:[3,6,7],develop:4,diffbas:[0,1,7],differ:1,differenc:4,different:0,differenti:[1,2,3,4,6],diffresult:[0,1],dimens:0,dimension:[3,7],directori:0,disabl:0,discuss:3,divid:0,document:[0,4,6,7],doe:[0,1,2,3],doesn:[0,6],don:[2,3],done:2,doubl:0,down:2,downstream:0,drastic:1,dual:[0,2],dualtest:2,dure:0,dynam:0,each:[0,3],easi:[2,3],easiest:2,easili:[0,1],element:[0,2],elementari:3,eltyp:0,emit:0,enabl:[0,3,4],encourag:3,end:[0,3,7],enough:6,entir:[0,3],erron:0,error:[0,7],essenti:[0,2],evalu:[0,3,4],even:0,evenli:0,everyth:2,exactli:1,examin:3,exampl:[0,2,3,6,7],except:6,execut:[0,3],exist:[0,2,7],exp2:2,exp:2,expens:0,explic:0,explicit:6,explicitli:[0,7],explictli:1,expm1:2,extend:3,extern:6,extra:[0,1,3],extract:3,fadd:0,fairli:2,famili:1,familiar:3,featur:[0,3,7],feed:1,fell:0,few:[2,7],field:3,find:4,fine:0,finit:4,first:0,flag:0,flexibl:[1,7],float64:0,follow:[3,4],fork:2,form:[0,1],forward:[3,4],forwarddiff:0,free:0,from:[0,2,3,4],fulli:7,futur:0,gener:[0,2,3,4,6,7],getelementptr:0,github:[2,4],gradient:0,gradientconfig:[0,1,7],guess:0,have:[0,2,3,7],heavili:0,help:7,here:[0,2,3,6,7],hessianconfig:1,hessianresult:7,heurist:0,higher:[0,3,4],highli:1,hold:0,horribl:0,how:0,howev:[0,1],http:4,hyper:3,i32:0,i64:0,ident:0,imag:0,improv:2,inbound:0,incorrectli:0,increas:0,inform:[0,1,3,6],inher:0,inject:6,input:[0,1,3,6],insert:0,instal:5,installat:4,instanc:[1,3],instead:[0,1,6,7],instruct:0,int64:0,intern:7,introduc:3,inv:2,isinf:0,isnan:0,jacobian:0,jacobianconfig:1,job:3,journal:4,julia:[0,2,3,4,5,6],julia_:0,kei:3,kindli:4,know:0,larger:0,lead:7,learn:[0,3,4,7],length:[0,7],less:7,let:[0,3],librari:0,like:[1,3,6],likewis:0,limit:4,link:2,list:[2,6],llvm:0,load:0,local:0,locat:0,log10:2,log1p:2,log2:2,log:[0,2],longer:7,look:[2,3],lubin:4,machin:0,magic:7,mai:[1,2,6],maintain:7,major:0,make:[0,2,3,6],manag:5,mani:0,manual:[0,1],master:2,mathemat:3,matrix:[0,1],mean:6,memori:[0,1],mention:2,merg:2,method:[0,1,4],might:0,mirror:0,mode:[0,3,4],modifi:1,modul:0,more:[0,1,7],multipl:3,multipli:[0,3],multithread:4,multithreadconfig:7,must:[6,7],mutat:[0,7],name:2,namespac:7,nansafe_mode_enabled:0,nativ:4,natur:[3,6],need:[0,2],nest:3,newcom:2,noalia:0,non:[2,4,6],note:[0,1],noth:1,notic:0,now:7,ntupl:3,number:[0,2],numer:3,object:4,obvious:2,occur:7,offici:6,old:7,older:4,omit:1,once:2,one:0,onli:[0,6],oop:0,open:2,oper:0,optim:[0,2],org:4,origin:3,other:[0,1,3,4,7],otherwis:0,our:[0,3,7],out:[0,1,7],outlin:2,outperform:4,output:[0,1,3],output_length:7,over:4,overcom:0,overload:3,overtop:3,own:[0,7],packag:[0,2,3,5,7],page:4,papamark:4,paper:4,paramet:[1,3],pariti:7,part:2,partial:[0,3],pass:[1,2,3],perform:[0,1,4],performantli:3,perhap:0,perturb:[0,1,3],pick:[0,2],pkg:[2,5],place:[0,2,7],plan:0,pleas:0,plethora:3,poison:0,possibl:[0,1],pre:0,prealloc:1,preserv:0,prevent:[0,1,3],primal:0,process:[0,2,3],program:6,propag:[0,6],properti:3,propog:3,proport:0,provid:[0,1,2,3,7],qualifi:7,question:0,rand:0,rather:0,reader:3,real:[1,3,6],realiti:[0,3],realli:4,reason:0,rebuild:0,recomend:1,reduc:[0,1],redund:0,refer:[6,7],rehash:3,reimplement:7,relev:[0,2],reli:0,remain:0,remaind:0,replac:2,repositori:2,repres:3,request:4,requir:[0,1,2,4],reshap:[0,7],resolv:2,resourc:4,ret:0,retriev:[0,4],reus:1,revel:4,revelslubinpapamarkou2016:4,revolv:2,roadblock:6,rosenbrock:0,rule:[2,3,6],run:[0,2,6],runtim:[0,6],sacrif:0,safe:0,sai:0,sake:1,same:2,scope:0,scroll:2,second:[0,1,3],section:[1,2],see:[0,1,2,6,7],seed:[1,3],seen:[0,6],select:[0,1],sensit:0,set:[0,4],sever:[0,1],shape:[0,1],share:2,should:[0,2],show:0,signatur:6,similar:[0,3],simpl:3,simpli:5,sin:[2,3],sinc:[0,7],singl:6,slpvectorizerpass:0,smaller:0,some:[0,2,6,7],sourc:0,special:2,specif:[0,1,6],specifi:1,speed:4,speedup:0,sqrt:2,squar:0,src:[0,2],sret:0,stabil:0,stabl:0,start:0,state:0,storag:[1,6],store:[0,1,3],struct:3,style:6,submit:2,substanti:2,subtyp:6,suit:[0,2],suitabl:2,support:[0,2,3,5,6,7],sure:2,swoop:0,symbol:2,system:0,tag:[0,3],take:[0,1,3,4,7],tan:2,target:[0,1,3,6],task:0,techniqu:[0,4,6],tensor:[0,7],term:3,test:2,than:0,thei:2,them:[1,3,7],thi:[0,1,2,3,4,6,7],thing:0,third:3,though:1,through:[2,6],thu:[0,3,6,7],time:0,titl:[2,4],top:0,transform:7,tune:0,tutori:2,two:[0,2,3],type:0,undefin:0,understand:3,unexport:4,unstabl:0,upgrade:4,url:4,usag:1,use:1,user:[0,1,3,4,6,7],usual:0,usualli:0,util:0,vari:4,variabl:0,vector_hessian:0,version:[0,4],via:0,wai:[0,2,3],want:0,well:[0,3,6],what:[0,2],whatev:2,when:[0,1,2],where:[0,1,3],wherea:0,whether:0,which:[0,1,2,3],wikipedia:4,wish:2,within:[3,6],without:[0,2,3],won:3,word:0,work:[0,1,2],workflow:2,would:[0,3],write:[0,2],written:6,year:4,yield:0,you:[0,1,2,4,7],your:[0,2,4,6],yourself:[1,2],zero:0},titles:["Advanced Usage Guide","Basic ForwardDiff API","How to Contribute","How ForwardDiff Works","ForwardDiff.jl","Installation and Version Requirements","Limitations of ForwardDiff","Upgrading from Older Versions of ForwardDiff"],titleterms:{"function":[0,2,7],"new":2,"public":4,"return":0,abstractconfig:1,access:0,adding:2,advanc:0,api:[1,3,7],basic:1,calculu:2,chunk:[0,7],configur:0,contribut:2,creat:7,deriv:1,differenti:7,dual:3,enabl:7,fix:0,forwarddiff:[1,2,3,4,6,7],from:7,gradient:1,guid:0,hessian:[0,1],higher:7,how:[2,3],implement:[2,3],inf:0,installat:5,issu:0,jacobian:1,limit:6,lower:[0,7],manual:2,multithread:7,nan:0,number:3,older:7,optimiz:2,order:[0,7],requir:5,result:[0,7],retriev:7,set:7,simd:0,size:[0,7],type:1,unari:2,unexport:7,upgrade:7,usage:0,valu:0,vector:0,version:[5,7],via:2,work:3}}) \ No newline at end of file +Search.setIndex({envversion:49,filenames:["basic_api","contributing","index"],objects:{ForwardDiff:{GradientConfig:[0,0,1,""],HessianConfig:[0,0,1,""],JacobianConfig:[0,0,1,""],derivative:[0,0,1,""],gradient:[0,0,1,""],hessian:[0,0,1,""],jacobian:[0,0,1,""]}},objnames:{"0":["py","function","Python function"]},objtypes:{"0":"py:function"},terms:{"case":[0,1],"catch":0,"default":0,"function":[0,1,2],"return":[0,2],"while":[0,2],abil:0,about:2,abstractarrai:0,access:2,accomplish:1,accuraci:2,add:1,advanc:2,advantag:2,advis:0,after:1,algorithm:2,all:[0,1],alloc:0,allow:0,also:[0,1],ani:[0,2],appli:1,apply:1,appropri:1,arg:0,argument:0,ariti:1,around:1,articl:2,arxiv:2,assum:0,attempt:1,author:2,auto:1,automat:[0,1,2],base:0,below:0,both:2,branch:1,brief:1,buffer:0,bundl:0,call:[0,1],callabl:2,can:[0,1,2],cannot:0,categori:1,cfg:0,chang:1,check:1,chunk:[0,2],cite:2,code:1,common:2,comput:0,configur:[0,2],confus:0,construct:0,constructor:0,contain:0,conveni:[0,1],creat:2,decreas:0,defin:1,deliv:1,depend:[1,2],derivativeconfig:0,desir:1,develop:2,diffbas:[0,1],differ:0,differenc:2,differenti:[0,2],diffresult:0,document:2,doe:0,done:1,drastic:0,dual:[1,2],each:[],easi:1,easili:0,element:[],enabl:2,evalu:2,everi:1,exactli:0,expand:1,explictli:0,extra:0,fairli:1,famili:0,feed:0,few:1,find:2,finit:2,fix:2,flexibl:0,follow:[1,2],fork:1,form:0,forward:2,from:2,gener:[1,2],github:[1,2],gradientconfig:0,guid:2,help:1,here:1,hessianconfig:0,higher:2,highli:0,howev:0,http:2,implement:[1,2],improv:1,includ:1,inf:2,inform:0,input:0,installat:2,instanc:[0,1],instead:0,issu:2,jacobianconfig:0,journal:2,julia:2,kindli:2,learn:2,like:0,limit:2,list:1,loop:1,lower:2,lubin:2,mai:0,maintain:1,make:1,manual:0,master:1,matrix:0,mechan:1,memori:0,merg:1,method:[0,2],might:1,mode:2,modifi:0,more:0,multithread:2,name:1,nan:2,nativ:2,need:1,newcom:1,non:2,note:0,noth:0,number:[1,2],object:2,older:2,omit:0,order:[0,1,2],org:2,other:[0,2],out:[0,1],outlin:1,outperform:2,output:0,over:[1,2],packag:1,page:2,papamark:2,paper:2,paramet:0,pass:0,perform:[0,2],perturb:0,possibl:0,prealloc:0,prevent:0,provid:[0,1],real:0,realinterfac:1,realli:2,recomend:0,reduc:0,relev:1,request:2,requir:[0,2],resourc:2,respect:[],result:[0,1,2],retriev:2,reus:0,revel:2,revelslubinpapamarkou2016:2,revolv:1,rule:1,sake:0,second:0,section:[0,1],see:0,seed:0,select:0,set:2,sever:0,shape:0,should:1,simd:2,simpl:1,size:[0,2],specif:0,specifi:0,speed:2,storag:0,store:0,submit:1,substanti:1,sure:1,symbol:1,take:[0,2],target:0,techniqu:2,test:1,thei:1,them:0,thi:[0,1,2],though:0,thu:1,titl:2,tupl:[],tutori:1,unexport:2,upgrade:2,url:2,usag:0,usage:2,use:0,user:[0,2],valu:[0,2],vari:2,vector:[0,2],version:2,via:1,wai:1,whatev:1,when:[0,1],where:0,which:0,wikipedia:2,work:[0,2],workflow:1,write:1,year:2,you:[0,1,2],your:[1,2],yourself:0},titles:["Basic ForwardDiff API","How to Contribute","ForwardDiff.jl"],titleterms:{"new":1,"public":2,abstractconfig:0,adding:1,api:0,basic:0,contribut:1,definit:1,deriv:[0,1],forwarddiff:[0,2],gradient:0,hessian:0,how:1,jacobian:0,type:0}}) \ No newline at end of file diff --git a/src/ForwardDiff.jl b/src/ForwardDiff.jl index fc86116f..1f4058cd 100644 --- a/src/ForwardDiff.jl +++ b/src/ForwardDiff.jl @@ -6,40 +6,19 @@ using DiffBase using DiffBase: DiffResult using StaticArrays -import Calculus import NaNMath import SpecialFunctions -import Base.Threads +import RealInterface +import CommonSubexpressions ############################# # types/functions/constants # ############################# -# NaN-safe mode switch # -#----------------------# - const NANSAFE_MODE_ENABLED = false -# function generation # -#---------------------# - -const AUTO_DEFINED_UNARY_FUNCS = map(first, Calculus.symbolic_derivatives_1arg()) - -const NANMATH_FUNCS = (:sin, :cos, :tan, :asin, :acos, :acosh, - :atanh, :log, :log2, :log10, :lgamma, :log1p) - -const SPECIAL_FUNCS = (:erf, :erfc, :erfinv, :erfcinv, :erfi, :erfcx, - :dawson, :digamma, :eta, :zeta, :airyai, :airyaiprime, - :airybi, :airybiprime, :airyaix, :besselj, :besselj0, - :besselj1, :besseljx, :bessely, :bessely0, :bessely1, - :besselyx, :besselh, :hankelh1, :hankelh1x, :hankelh2, - :hankelh2x, :besseli, :besselix, :besselk, :besselkx) - const REAL_TYPES = (AbstractFloat, Irrational, Integer, Rational, Real) -# chunk settings # -#----------------# - const DEFAULT_CHUNK_THRESHOLD = 10 struct Chunk{N} end diff --git a/src/dual.jl b/src/dual.jl index af01c9d3..e3b083b7 100644 --- a/src/dual.jl +++ b/src/dual.jl @@ -83,9 +83,9 @@ end @inline tagtype(::Dual{T,V,N}) where {T,V,N} = T @inline tagtype(::Type{Dual{T,V,N}}) where {T,V,N} = T -##################################### -# N-ary Operation Definition Macros # -##################################### +#################################### +# N-ary Operation Definition Tools # +#################################### macro define_binary_dual_op(f, xy_body, x_body, y_body) defs = quote @@ -158,6 +158,57 @@ macro define_ternary_dual_op(f, xyz_body, xy_body, xz_body, yz_body, x_body, y_b return esc(defs) end +function unary_dual_definition(M, f) + work = CommonSubexpressions.cse(quote + val = $(f)(x) + deriv = $(DiffBase.diffrule(f, :x)) + end) + return quote + @inline function ($M).$(f)(d::Dual{T}) where T + x = value(d) + $work + return Dual{T}(val, deriv * partials(d)) + end + end +end + +function binary_dual_definition(M, f) + dvx, dvy = DiffBase.diffrule(f, :vx, :vy) + xy_work = CommonSubexpressions.cse(quote + val = $(f)(vx, vy) + dvx = $dvx + dvy = $dvy + end) + x_work = CommonSubexpressions.cse(quote + val = $(f)(vx, vy) + dvx = $dvx + end) + y_work = CommonSubexpressions.cse(quote + val = $(f)(vx, vy) + dvy = $dvy + end) + return quote + @define_binary_dual_op( + ($M).$(f), + begin + vx, vy = value(x), value(y) + $xy_work + return Dual{T}(val, _mul_partials(partials(x), partials(y), dvx, dvy)) + end, + begin + vx, vy = value(x), value(y) + $x_work + return Dual{T}(val, dvx * partials(x)) + end, + begin + vx, vy = value(x), value(y) + $y_work + return Dual{T}(val, dvy * partials(y)) + end + ) + end +end + ##################### # Generic Functions # ##################### @@ -211,7 +262,11 @@ end isconstant(d::Dual) = iszero(partials(d)) -for pred in (:isequal, :(==), :isless, :(<=), :<) +for pred in RealInterface.UNARY_PREDICATES + @eval Base.$(pred)(d::Dual) = $(pred)(value(d)) +end + +for pred in RealInterface.BINARY_PREDICATES @eval begin @define_binary_dual_op( Base.$(pred), @@ -222,14 +277,6 @@ for pred in (:isequal, :(==), :isless, :(<=), :<) end end -Base.isnan(d::Dual) = isnan(value(d)) -Base.isfinite(d::Dual) = isfinite(value(d)) -Base.isinf(d::Dual) = isinf(value(d)) -Base.isreal(d::Dual) = isreal(value(d)) -Base.isinteger(d::Dual) = isinteger(value(d)) -Base.iseven(d::Dual) = iseven(value(d)) -Base.isodd(d::Dual) = isodd(value(d)) - ######################## # Promotion/Conversion # ######################## @@ -258,9 +305,9 @@ Base.promote_array_type(F, ::Type{<:AbstractFloat}, ::Type{<:Dual}, ::Type{P}) w Base.float(d::Dual{T,V,N}) where {T,V,N} = Dual{T,promote_type(V, Float16),N}(d) Base.AbstractFloat(d::Dual{T,V,N}) where {T,V,N} = Dual{T,promote_type(V, Float16),N}(d) -######## -# Math # -######## +############## +# Arithmetic # +############## # Addition/Subtraction # #----------------------# @@ -345,77 +392,55 @@ for f in (:(Base.:^), :(NaNMath.pow)) end end -# Unary Math Functions # -#--------------------- # - -function to_nanmath(x::Expr) - if x.head == :call - funsym = Expr(:., :NaNMath, Base.Meta.quot(x.args[1])) - return Expr(:call, funsym, [to_nanmath(z) for z in x.args[2:end]]...) - else - return Expr(:call, [to_nanmath(z) for z in x.args]...) - end -end - -to_nanmath(x) = x +################################### +# General Mathematical Operations # +################################### @inline Base.conj(d::Dual) = d + @inline Base.transpose(d::Dual) = d + @inline Base.ctranspose(d::Dual) = d + @inline Base.abs(d::Dual) = signbit(value(d)) ? -d : d -for fsym in AUTO_DEFINED_UNARY_FUNCS - v = :v - deriv = Calculus.differentiate(:($(fsym)($v)), v) - - # exp and sqrt are manually defined below - if !(in(fsym, (:exp, :sqrt))) - funcs = Vector{Expr}(0) - is_special_function = in(fsym, SPECIAL_FUNCS) - is_special_function && push!(funcs, :(SpecialFunctions.$(fsym))) - (!(is_special_function) || VERSION < v"0.6.0-dev.2767") && push!(funcs, :(Base.$(fsym))) - for func in funcs - @eval begin - @inline function $(func)(d::Dual{T}) where T - $(v) = value(d) - return Dual{T}($(func)($v), $(deriv) * partials(d)) - end - end - end - end +for f in RealInterface.UNARY_MATH + DiffBase.hasdiffrule(f, 1) && eval(unary_dual_definition(Base, f)) +end - # extend corresponding NaNMath methods - if fsym in NANMATH_FUNCS - nan_deriv = to_nanmath(deriv) - @eval begin - @inline function NaNMath.$(fsym)(d::Dual{T}) where T - v = value(d) - return Dual{T}(NaNMath.$(fsym)($v), $(nan_deriv) * partials(d)) +for f in RealInterface.UNARY_SPECIAL_MATH + DiffBase.hasdiffrule(f, 1) && eval(unary_dual_definition(SpecialFunctions, f)) +end + +for f in RealInterface.BINARY_SPECIAL_MATH + DiffBase.hasdiffrule(f, 2) && eval(binary_dual_definition(SpecialFunctions, f)) +end + +function to_nanmath!(x) + if isa(x, Expr) + if x.head == :call + f = x.args[1] + if in(f, RealInterface.UNARY_NAN_MATH) || in(f, RealInterface.BINARY_NAN_MATH) + x.args[1] = :(NaNMath.$f) end + foreach(to_nanmath!, x.args[2:end]) + else + foreach(to_nanmath!, x.args) end end + return x +end + +for f in RealInterface.UNARY_NAN_MATH + DiffBase.hasdiffrule(f, 1) && eval(to_nanmath!(unary_dual_definition(NaNMath, f))) end ################# # Special Cases # ################# -# exp - -@inline function Base.exp(d::Dual{T}) where T - expv = exp(value(d)) - return Dual{T}(expv, expv * partials(d)) -end - -# sqrt - -@inline function Base.sqrt(d::Dual{T}) where T - sqrtv = sqrt(value(d)) - deriv = inv(sqrtv + sqrtv) - return Dual{T}(sqrtv, deriv * partials(d)) -end - -# hypot +# hypot # +#-------# @inline function calc_hypot(x, y, ::Type{T}) where T vx = value(x) @@ -451,7 +476,8 @@ end calc_hypot(x, y, z, T), ) -# atan2 +# atan2 # +#-------# @inline function calc_atan2(y, x, ::Type{T}) where T z = y / x @@ -468,7 +494,8 @@ end calc_atan2(x, y, T) ) -# fma +# fma # +#-----# @generated function calc_fma_xyz(x::Dual{T,<:Real,N}, y::Dual{T,<:Real,N}, @@ -509,7 +536,8 @@ end Dual{T}(fma(x, y, value(z)), partials(z)) # z_body ) -# sincos +# sincos # +#--------# @inline sincos(x) = (sin(x), cos(x)) diff --git a/test/DualTest.jl b/test/DualTest.jl index c8f91765..2dafd321 100644 --- a/test/DualTest.jl +++ b/test/DualTest.jl @@ -7,6 +7,7 @@ using ForwardDiff: Partials, Dual, value, partials import NaNMath import Calculus import SpecialFunctions +import RealInterface samerng() = MersenneTwister(1) @@ -308,12 +309,12 @@ for N in (0,3), M in (0,4), V in (Int, Float32) @test Base.promote_array_type(+, V, Dual{Void,Int,N}) == Dual{Void,V,N} end - ######## - # Math # - ######## - + ############## # Arithmetic # - #------------# + ############## + + # Addition/Subtraction # + #----------------------# @test FDNUM + FDNUM2 === Dual(value(FDNUM) + value(FDNUM2), partials(FDNUM) + partials(FDNUM2)) @test FDNUM + PRIMAL === Dual(value(FDNUM) + PRIMAL, partials(FDNUM)) @@ -333,6 +334,9 @@ for N in (0,3), M in (0,4), V in (Int, Float32) @test PRIMAL - NESTED_FDNUM === Dual(PRIMAL - value(NESTED_FDNUM), -(partials(NESTED_FDNUM))) @test -(NESTED_FDNUM) === Dual(-(value(NESTED_FDNUM)), -(partials(NESTED_FDNUM))) + # Multiplication # + #----------------# + @test FDNUM * FDNUM2 === Dual(value(FDNUM) * value(FDNUM2), ForwardDiff._mul_partials(partials(FDNUM), partials(FDNUM2), value(FDNUM2), value(FDNUM))) @test FDNUM * PRIMAL === Dual(value(FDNUM) * PRIMAL, partials(FDNUM) * PRIMAL) @test PRIMAL * FDNUM === Dual(value(FDNUM) * PRIMAL, partials(FDNUM) * PRIMAL) @@ -341,6 +345,9 @@ for N in (0,3), M in (0,4), V in (Int, Float32) @test NESTED_FDNUM * PRIMAL === Dual(value(NESTED_FDNUM) * PRIMAL, partials(NESTED_FDNUM) * PRIMAL) @test PRIMAL * NESTED_FDNUM === Dual(value(NESTED_FDNUM) * PRIMAL, partials(NESTED_FDNUM) * PRIMAL) + # Division # + #----------# + if M > 0 && N > 0 @test Dual{1}(FDNUM) / Dual{1}(PRIMAL) === Dual{1}(FDNUM / PRIMAL) @test Dual{1}(PRIMAL) / Dual{1}(FDNUM) === Dual{1}(PRIMAL / FDNUM) @@ -357,6 +364,9 @@ for N in (0,3), M in (0,4), V in (Int, Float32) @test dual_isapprox(NESTED_FDNUM / PRIMAL, Dual(value(NESTED_FDNUM) / PRIMAL, partials(NESTED_FDNUM) / PRIMAL)) @test dual_isapprox(PRIMAL / NESTED_FDNUM, Dual(PRIMAL / value(NESTED_FDNUM), (-(PRIMAL) / value(NESTED_FDNUM)^2) * partials(NESTED_FDNUM))) + # Exponentiation # + #----------------# + @test dual_isapprox(FDNUM^FDNUM2, exp(FDNUM2 * log(FDNUM))) @test dual_isapprox(FDNUM^PRIMAL, exp(PRIMAL * log(FDNUM))) @test dual_isapprox(PRIMAL^FDNUM, exp(FDNUM * log(PRIMAL))) @@ -367,13 +377,16 @@ for N in (0,3), M in (0,4), V in (Int, Float32) @test partials(NaNMath.pow(Dual(-2.0, 1.0), Dual(2.0, 0.0)), 1) == -4.0 - # Unary Functions # - #-----------------# + ################################### + # General Mathematical Operations # + ################################### @test conj(FDNUM) === FDNUM @test conj(NESTED_FDNUM) === NESTED_FDNUM + @test transpose(FDNUM) === FDNUM @test transpose(NESTED_FDNUM) === NESTED_FDNUM + @test ctranspose(FDNUM) === FDNUM @test ctranspose(NESTED_FDNUM) === NESTED_FDNUM @@ -383,36 +396,41 @@ for N in (0,3), M in (0,4), V in (Int, Float32) @test abs(NESTED_FDNUM) === NESTED_FDNUM if V != Int - UNSUPPORTED_NESTED_FUNCS = (:trigamma, :airyprime, :besselj1, :bessely1) - DOMAIN_ERR_FUNCS = (:asec, :acsc, :asecd, :acscd, :acoth, :acosh) - - for fsym in ForwardDiff.AUTO_DEFINED_UNARY_FUNCS - try - v = :v - deriv = Calculus.differentiate(:($(fsym)($v)), v) - is_nanmath_func = in(fsym, ForwardDiff.NANMATH_FUNCS) - is_special_func = in(fsym, ForwardDiff.SPECIAL_FUNCS) - is_domain_err_func = in(fsym, DOMAIN_ERR_FUNCS) - is_unsupported_nested_func = in(fsym, UNSUPPORTED_NESTED_FUNCS) - tested_funcs = Vector{Expr}(0) - is_nanmath_func && push!(tested_funcs, :(NaNMath.$(fsym))) - is_special_func && push!(tested_funcs, :(SpecialFunctions.$(fsym))) - (!(is_special_func) || VERSION < v"0.6.0-dev.2767") && push!(tested_funcs, :(Base.$(fsym))) - for func in tested_funcs - @eval begin - fdnum = $(is_domain_err_func ? FDNUM + 1 : FDNUM) - $(v) = ForwardDiff.value(fdnum) - @test dual_isapprox($(func)(fdnum), ForwardDiff.Dual($(func)($v), $(deriv) * ForwardDiff.partials(fdnum))) - if $(!(is_unsupported_nested_func)) - nested_fdnum = $(is_domain_err_func ? NESTED_FDNUM + 1 : NESTED_FDNUM) - $(v) = ForwardDiff.value(nested_fdnum) - @test dual_isapprox($(func)(nested_fdnum), ForwardDiff.Dual($(func)($v), $(deriv) * ForwardDiff.partials(nested_fdnum))) - end + for f in vcat(RealInterface.UNARY_MATH, RealInterface.UNARY_SPECIAL_MATH) + if DiffBase.hasdiffrule(f, 1) + deriv = DiffBase.diffrule(f, :x) + modifier = in(f, (:asec, :acsc, :asecd, :acscd, :acosh, :acoth)) ? one(V) : zero(V) + @eval begin + x = rand() + $modifier + dx = $f(Dual(x, one(x))) + @test value(dx) == $f(x) + @test partials(dx, 1) == $deriv + end + end + end + for f in RealInterface.BINARY_SPECIAL_MATH + in(f, (:hankelh1, :hankelh1x, :hankelh2, :hankelh2x)) && continue + if DiffBase.hasdiffrule(f, 2) + derivs = DiffBase.diffrule(f, :x, :y) + @eval begin + x, y = rand(1:10), rand() + dx = $f(Dual(x, one(x)), y) + dy = $f(x, Dual(y, one(y))) + actualdx = $(derivs[1]) + actualdy = $(derivs[2]) + @test value(dx) == $f(x, y) + @test value(dy) == value(dx) + if isnan(actualdx) + @test isnan(partials(dx, 1)) + else + @test partials(dx, 1) == actualdx + end + if isnan(actualdy) + @test isnan(partials(dy, 1)) + else + @test partials(dy, 1) == actualdy end end - catch err - warn("Encountered error when testing $(fsym)(::Dual):") - rethrow(err) end end end @@ -430,6 +448,8 @@ for N in (0,3), M in (0,4), V in (Int, Float32) @test typeof(sqrt(NESTED_FDNUM)) === typeof(NESTED_FDNUM) end + @test dual_isapprox(atan2(abs(FDNUM), abs(FDNUM2)), atan(abs(FDNUM) / abs(FDNUM2))) + @test dual_isapprox(fma(FDNUM, FDNUM2, FDNUM3), Dual(fma(PRIMAL, PRIMAL2, PRIMAL3), PRIMAL*PARTIALS2 + PRIMAL2*PARTIALS + PARTIALS3)) @test dual_isapprox(fma(FDNUM, FDNUM2, PRIMAL3), Dual(fma(PRIMAL, PRIMAL2, PRIMAL3), PRIMAL*PARTIALS2 + PRIMAL2*PARTIALS)) @test dual_isapprox(fma(PRIMAL, FDNUM2, FDNUM3), Dual(fma(PRIMAL, PRIMAL2, PRIMAL3), PRIMAL*PARTIALS2 + PARTIALS3)) diff --git a/test/REQUIRE b/test/REQUIRE new file mode 100644 index 00000000..a200ff4a --- /dev/null +++ b/test/REQUIRE @@ -0,0 +1 @@ +Calculus 0.2.0