diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index 5744bc1d2..d896f074b 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.11.2","generation_timestamp":"2024-12-17T11:16:08","documenter_version":"1.8.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.11.2","generation_timestamp":"2024-12-22T12:24:38","documenter_version":"1.8.0"}} \ No newline at end of file diff --git a/dev/assets/Manifest.toml b/dev/assets/Manifest.toml index 471fdbdd4..0c18602ff 100644 --- a/dev/assets/Manifest.toml +++ b/dev/assets/Manifest.toml @@ -455,9 +455,9 @@ version = "1.11.0" [[deps.Distributions]] deps = ["AliasTables", "FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SpecialFunctions", "Statistics", "StatsAPI", "StatsBase", "StatsFuns"] -git-tree-sha1 = "9d9e93d19c912ee6f0f3543af0d8839079dbd0d7" +git-tree-sha1 = "4b138e4643b577ccf355377c2bc70fa975af25de" uuid = "31c24e10-a181-5473-b8eb-7969acd0382f" -version = "0.25.114" +version = "0.25.115" [deps.Distributions.extensions] DistributionsChainRulesCoreExt = "ChainRulesCore" @@ -571,9 +571,9 @@ version = "4.4.4+1" [[deps.FLINT_jll]] deps = ["Artifacts", "GMP_jll", "JLLWrappers", "Libdl", "MPFR_jll", "OpenBLAS32_jll"] -git-tree-sha1 = "4bde447e7bc6de73de36870fc942df02f4dd44b5" +git-tree-sha1 = "9327ebef3e04034b2aa1b9c59869f36717843ce5" uuid = "e134572f-a0d5-539d-bddf-3cad8db41a82" -version = "300.100.300+0" +version = "300.100.301+0" [[deps.FastBroadcast]] deps = ["ArrayInterface", "LinearAlgebra", "Polyester", "Static", "StaticArrayInterface", "StrideArraysCore"] @@ -586,6 +586,12 @@ git-tree-sha1 = "acebe244d53ee1b461970f8910c235b259e772ef" uuid = "9aa1b823-49e4-5ca5-8b0f-3971ec8bab6a" version = "0.3.2" +[[deps.FastGaussQuadrature]] +deps = ["LinearAlgebra", "SpecialFunctions", "StaticArrays"] +git-tree-sha1 = "fd923962364b645f3719855c88f7074413a6ad92" +uuid = "442a2c76-b920-505d-bb47-c5924d526838" +version = "1.0.2" + [[deps.FastLapackInterface]] deps = ["LinearAlgebra"] git-tree-sha1 = "cbf5edddb61a43669710cbc2241bc08b36d9e660" @@ -682,9 +688,9 @@ version = "2.13.3+1" [[deps.FriBidi_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1ed150b39aebcc805c26b93a8d0122c940f64ce2" +git-tree-sha1 = "846f7026a9decf3679419122b49f8a1fdb48d2d5" uuid = "559328eb-81f9-559d-9380-de523a88c83c" -version = "1.0.14+0" +version = "1.0.16+0" [[deps.FunctionWrappers]] git-tree-sha1 = "d62485945ce5ae9c0c48f124a84998d755bae00e" @@ -704,9 +710,9 @@ version = "1.11.0" [[deps.GLFW_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Xorg_libXcursor_jll", "Xorg_libXi_jll", "Xorg_libXinerama_jll", "Xorg_libXrandr_jll", "libdecor_jll", "xkbcommon_jll"] -git-tree-sha1 = "532f9126ad901533af1d4f5c198867227a7bb077" +git-tree-sha1 = "fcb0584ff34e25155876418979d4c8971243bb89" uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89" -version = "3.4.0+1" +version = "3.4.0+2" [[deps.GMP_jll]] deps = ["Artifacts", "Libdl"] @@ -757,9 +763,9 @@ version = "2.47.1+0" [[deps.Glib_jll]] deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "48b5d4c75b2c9078ead62e345966fa51a25c05ad" +git-tree-sha1 = "b0036b392358c80d2d2124746c2bf3d48d457938" uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" -version = "2.82.2+1" +version = "2.82.4+0" [[deps.Graphite2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -790,9 +796,9 @@ weakdeps = ["DynamicPolynomials"] [[deps.HTTP]] deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "ExceptionUnwrapping", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "PrecompileTools", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"] -git-tree-sha1 = "627fcacdb7cb51dc67f557e1598cdffe4dda386d" +git-tree-sha1 = "c67b33b085f6e2faf8bf79a61962e7339a81129c" uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" -version = "1.10.14" +version = "1.10.15" [[deps.HarfBuzz_jll]] deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll"] @@ -895,9 +901,9 @@ version = "0.21.4" [[deps.JpegTurbo_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "25ee0be4d43d0269027024d75a24c24d6c6e590c" +git-tree-sha1 = "ef10afc9f4b942bcd75f4c3bc9d9e8d802944c23" uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" -version = "3.0.4+0" +version = "3.1.0+0" [[deps.KLU]] deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse_jll"] @@ -919,9 +925,9 @@ version = "3.100.2+0" [[deps.LERC_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "36bdbc52f13a7d1dcb0f3cd694e01677a515655b" +git-tree-sha1 = "4ec1e8fac04150b570e315baaa68950e368a803d" uuid = "88015f11-f218-50d7-93a8-a6af411a945d" -version = "4.0.0+0" +version = "4.0.0+1" [[deps.LLVMOpenMP_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -1039,9 +1045,9 @@ version = "1.7.0+0" [[deps.Libgpg_error_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "c6ce1e19f3aec9b59186bdf06cdf3c4fc5f5f3e6" +git-tree-sha1 = "a7f43994b47130e4f491c3b2dbe78fe9e2aed2b3" uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" -version = "1.50.0+0" +version = "1.51.0+0" [[deps.Libiconv_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -1361,9 +1367,9 @@ weakdeps = ["ForwardDiff"] NonlinearSolveSpectralMethodsForwardDiffExt = "ForwardDiff" [[deps.OffsetArrays]] -git-tree-sha1 = "39d000d9c33706b8364817d8894fae1548f40295" +git-tree-sha1 = "5e1897147d1ff8d98883cda2be2187dcf57d8f0c" uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" -version = "1.14.2" +version = "1.15.0" weakdeps = ["Adapt"] [deps.OffsetArrays.extensions] @@ -1433,16 +1439,16 @@ uuid = "89bda076-bce5-4f1c-845f-551c83cdda9a" version = "1.1.0" [[deps.OrdinaryDiffEqBDF]] -deps = ["ArrayInterface", "DiffEqBase", "FastBroadcast", "LinearAlgebra", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "OrdinaryDiffEqSDIRK", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "StaticArrays", "TruncatedStacktraces"] -git-tree-sha1 = "b4498d40bf35da0b6d22652ff2e9d8820590b3c6" +deps = ["ADTypes", "ArrayInterface", "DiffEqBase", "FastBroadcast", "LinearAlgebra", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "OrdinaryDiffEqSDIRK", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "StaticArrays", "TruncatedStacktraces"] +git-tree-sha1 = "86ccea2bd0fdfa5570172a9717061f6417e21dea" uuid = "6ad6398a-0878-4a85-9266-38940aa047c8" -version = "1.1.2" +version = "1.2.0" [[deps.OrdinaryDiffEqCore]] deps = ["ADTypes", "Accessors", "Adapt", "ArrayInterface", "DataStructures", "DiffEqBase", "DocStringExtensions", "EnumX", "FastBroadcast", "FastClosures", "FastPower", "FillArrays", "FunctionWrappersWrappers", "InteractiveUtils", "LinearAlgebra", "Logging", "MacroTools", "MuladdMacro", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "SciMLStructures", "SimpleUnPack", "Static", "StaticArrayInterface", "StaticArraysCore", "SymbolicIndexingInterface", "TruncatedStacktraces"] -git-tree-sha1 = "c7f395034602c3e4d40ece93dc2c9f066f0ce61f" +git-tree-sha1 = "72c77ae685fddb6291fff22dba13f4f32602475c" uuid = "bbf590c4-e513-4bbe-9b18-05decba2e5d8" -version = "1.13.0" +version = "1.14.1" weakdeps = ["EnzymeCore"] [deps.OrdinaryDiffEqCore.extensions] @@ -1456,9 +1462,9 @@ version = "1.1.0" [[deps.OrdinaryDiffEqDifferentiation]] deps = ["ADTypes", "ArrayInterface", "DiffEqBase", "FastBroadcast", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "LinearAlgebra", "LinearSolve", "OrdinaryDiffEqCore", "SciMLBase", "SparseArrays", "SparseDiffTools", "StaticArrayInterface", "StaticArrays"] -git-tree-sha1 = "8977f283a7d89c5d5c06c933467ed4af0a99f2f7" +git-tree-sha1 = "e4adff09a6d47b74c16e7e08f10fa32000e2e5ed" uuid = "4302a76b-040a-498a-8c04-15b101fed76b" -version = "1.2.0" +version = "1.3.0" [[deps.OrdinaryDiffEqExplicitRK]] deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "TruncatedStacktraces"] @@ -1467,22 +1473,22 @@ uuid = "9286f039-9fbf-40e8-bf65-aa933bdc4db0" version = "1.1.0" [[deps.OrdinaryDiffEqExponentialRK]] -deps = ["DiffEqBase", "ExponentialUtilities", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqSDIRK", "OrdinaryDiffEqVerner", "RecursiveArrayTools", "Reexport", "SciMLBase"] -git-tree-sha1 = "f63938b8e9e5d3a05815defb3ebdbdcf61ec0a74" +deps = ["ADTypes", "DiffEqBase", "ExponentialUtilities", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqSDIRK", "OrdinaryDiffEqVerner", "RecursiveArrayTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "8659d5fdfe0798bbb5bcd8dc8d08092744b6dfa4" uuid = "e0540318-69ee-4070-8777-9e2de6de23de" -version = "1.1.0" +version = "1.2.0" [[deps.OrdinaryDiffEqExtrapolation]] -deps = ["DiffEqBase", "FastBroadcast", "FastPower", "LinearSolve", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "Polyester", "RecursiveArrayTools", "Reexport"] -git-tree-sha1 = "048bcccc8f59c20d5b4ad268eef4d7d21c005a94" +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "FastPower", "LinearSolve", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "Polyester", "RecursiveArrayTools", "Reexport"] +git-tree-sha1 = "b8d852b23246b1427178520442e8e7d89aa1c64c" uuid = "becaefa8-8ca2-5cf9-886d-c06f3d2bd2c4" -version = "1.2.1" +version = "1.3.0" [[deps.OrdinaryDiffEqFIRK]] -deps = ["DiffEqBase", "FastBroadcast", "FastPower", "LinearAlgebra", "LinearSolve", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "RecursiveArrayTools", "Reexport", "SciMLOperators"] -git-tree-sha1 = "7a6e3996dc0850aee6cdc10c8afa377242fce702" +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "FastGaussQuadrature", "FastPower", "LinearAlgebra", "LinearSolve", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators"] +git-tree-sha1 = "39a52e278e21018585fc02c04a54f0ab98d03369" uuid = "5960d6e9-dd7a-4743-88e7-cf307b64f125" -version = "1.5.0" +version = "1.6.0" [[deps.OrdinaryDiffEqFeagin]] deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "RecursiveArrayTools", "Reexport", "Static"] @@ -1503,10 +1509,10 @@ uuid = "d28bc4f8-55e1-4f49-af69-84c1a99f0f58" version = "1.1.0" [[deps.OrdinaryDiffEqIMEXMultistep]] -deps = ["DiffEqBase", "FastBroadcast", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "Reexport"] -git-tree-sha1 = "9f8f52aad2399d7714b400ff9d203254b0a89c4a" +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "Reexport"] +git-tree-sha1 = "b35b6db127ad50bc502f72dbd5a84a1e1d1bbf01" uuid = "9f002381-b378-40b7-97a6-27a27c83f129" -version = "1.1.0" +version = "1.2.0" [[deps.OrdinaryDiffEqLinear]] deps = ["DiffEqBase", "ExponentialUtilities", "LinearAlgebra", "OrdinaryDiffEqCore", "OrdinaryDiffEqTsit5", "OrdinaryDiffEqVerner", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators"] @@ -1539,10 +1545,10 @@ uuid = "c9986a66-5c92-4813-8696-a7ec84c806c8" version = "1.1.0" [[deps.OrdinaryDiffEqPDIRK]] -deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "Polyester", "Reexport", "StaticArrays"] -git-tree-sha1 = "a8b7f8107c477e07c6a6c00d1d66cac68b801bbc" +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "Polyester", "Reexport", "StaticArrays"] +git-tree-sha1 = "70e348c116ce62df4e4b4f90f3c8bb4a8164df31" uuid = "5dd0a6cf-3d4b-4314-aa06-06d4e299bc89" -version = "1.1.0" +version = "1.2.0" [[deps.OrdinaryDiffEqPRK]] deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "Reexport"] @@ -1564,15 +1570,15 @@ version = "1.1.0" [[deps.OrdinaryDiffEqRosenbrock]] deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "LinearSolve", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "Static"] -git-tree-sha1 = "760a51a626d0065455847e4a3f788b07e86e5090" +git-tree-sha1 = "749518f27e886164ee07e6df49b631beaca8c9ac" uuid = "43230ef6-c299-4910-a778-202eb28ce4ce" -version = "1.3.1" +version = "1.4.0" [[deps.OrdinaryDiffEqSDIRK]] -deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "RecursiveArrayTools", "Reexport", "SciMLBase", "TruncatedStacktraces"] -git-tree-sha1 = "f6683803a58de600ab7a26d2f49411c9923e9721" +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "LinearAlgebra", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "RecursiveArrayTools", "Reexport", "SciMLBase", "TruncatedStacktraces"] +git-tree-sha1 = "45eed1444fbfa510e1806d4153f83bd862d2d035" uuid = "2d112036-d095-4a1e-ab9a-08536f3ecdbf" -version = "1.1.0" +version = "1.2.0" [[deps.OrdinaryDiffEqSSPRK]] deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "Static", "StaticArrays"] @@ -1581,10 +1587,10 @@ uuid = "669c94d9-1f4b-4b64-b377-1aa079aa2388" version = "1.2.0" [[deps.OrdinaryDiffEqStabilizedIRK]] -deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "RecursiveArrayTools", "Reexport", "StaticArrays"] -git-tree-sha1 = "348fd6def9a88518715425025eadd58517017325" +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "RecursiveArrayTools", "Reexport", "StaticArrays"] +git-tree-sha1 = "f2533f086540db6eb3a5eddbecf963cbc4ab6015" uuid = "e3e12d00-db14-5390-b879-ac3dd2ef6296" -version = "1.1.0" +version = "1.2.0" [[deps.OrdinaryDiffEqStabilizedRK]] deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "StaticArrays"] @@ -1629,9 +1635,9 @@ weakdeps = ["Requires", "TOML"] [[deps.Pango_jll]] deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl"] -git-tree-sha1 = "e127b609fb9ecba6f201ba7ab753d5a605d53801" +git-tree-sha1 = "ed6834e95bd326c52d5675b4181386dfbe885afb" uuid = "36c8627f-9965-5494-a995-c6b170f724f3" -version = "1.54.1+0" +version = "1.55.5+0" [[deps.Parameters]] deps = ["OrderedCollections", "UnPack"] @@ -1909,9 +1915,9 @@ version = "0.6.43" [[deps.SciMLBase]] deps = ["ADTypes", "Accessors", "ArrayInterface", "CommonSolve", "ConstructionBase", "Distributed", "DocStringExtensions", "EnumX", "Expronicon", "FunctionWrappersWrappers", "IteratorInterfaceExtensions", "LinearAlgebra", "Logging", "Markdown", "PrecompileTools", "Preferences", "Printf", "RecipesBase", "RecursiveArrayTools", "Reexport", "RuntimeGeneratedFunctions", "SciMLOperators", "SciMLStructures", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface"] -git-tree-sha1 = "213408a448e27170e4fca428838b8d11c5bbf1ab" +git-tree-sha1 = "fd0794f59136050f4734f7af7818fec2f82bdcfc" uuid = "0bca4576-84f4-4d90-8ffe-ffa030f20462" -version = "2.68.1" +version = "2.69.0" [deps.SciMLBase.extensions] SciMLBaseChainRulesCoreExt = "ChainRulesCore" @@ -2102,9 +2108,9 @@ weakdeps = ["OffsetArrays", "StaticArrays"] [[deps.StaticArrays]] deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] -git-tree-sha1 = "777657803913ffc7e8cc20f0fd04b634f871af8f" +git-tree-sha1 = "7c01731da8ab6d3094c4d44c9057b00932459255" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.9.8" +version = "1.9.9" weakdeps = ["ChainRulesCore", "Statistics"] [deps.StaticArrays.extensions] @@ -2361,9 +2367,9 @@ version = "1.3.243+0" [[deps.Wayland_jll]] deps = ["Artifacts", "EpollShim_jll", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "7558e29847e99bc3f04d6569e82d0f5c54460703" +git-tree-sha1 = "85c7811eddec9e7f22615371c3cc81a504c508ee" uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89" -version = "1.21.0+1" +version = "1.21.0+2" [[deps.Wayland_protocols_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -2414,10 +2420,10 @@ uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" version = "1.0.11+1" [[deps.Xorg_libXcursor_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"] -git-tree-sha1 = "12e0eb3bc634fa2080c1c37fccf56f7c22989afd" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "807c226eaf3651e7b2c468f687ac788291f9a89b" uuid = "935fb764-8cf2-53bf-bb30-45bb1f8bf724" -version = "1.2.0+4" +version = "1.2.3+0" [[deps.Xorg_libXdmcp_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -2432,16 +2438,16 @@ uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" version = "1.3.6+1" [[deps.Xorg_libXfixes_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "6fcc21d5aea1a0b7cce6cab3e62246abd1949b86" uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" -version = "5.0.3+4" +version = "6.0.0+0" [[deps.Xorg_libXi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXfixes_jll"] -git-tree-sha1 = "89b52bc2160aadc84d707093930ef0bffa641246" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXext_jll", "Xorg_libXfixes_jll"] +git-tree-sha1 = "984b313b049c89739075b8e2a94407076de17449" uuid = "a51aa0fd-4e3c-5386-b890-e753decda492" -version = "1.7.10+4" +version = "1.8.2+0" [[deps.Xorg_libXinerama_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXext_jll"] @@ -2651,6 +2657,6 @@ version = "3.5.0+0" [[deps.xkbcommon_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll", "Wayland_protocols_jll", "Xorg_libxcb_jll", "Xorg_xkeyboard_config_jll"] -git-tree-sha1 = "9c304562909ab2bab0262639bd4f444d7bc2be37" +git-tree-sha1 = "63406453ed9b33a0df95d570816d5366c92b7809" uuid = "d8fb68d0-12a3-5cfd-a85a-d49703b185fd" -version = "1.4.1+1" +version = "1.4.1+2" diff --git a/dev/comparison/index.html b/dev/comparison/index.html index 95921a920..88c8649b2 100644 --- a/dev/comparison/index.html +++ b/dev/comparison/index.html @@ -1,2 +1,2 @@ -Comparison Against SymPy · Symbolics.jl

Comparison of Julia's Symbolics.jl vs SymPy for Symbolic Computation

Symbolics.jl is a symbolic modeling language for Julia, built in Julia. Its goal is very different from Sympy: it was made to support symbolic-numerics, the combination of symbolic computing with numerical methods to allow for extreme performance computing that would not be possible without modifying the model. Because of this, Symbolics.jl excels in many areas due to purposeful design decisions:

  • Performance: Symbolics.jl is built in Julia, whereas SymPy was built in Python. Thus, the performance bar for Symbolics.jl is much higher. Symbolics.jl started because SymPy was far too slow and SymEngine was far too inflexible for the projects they were doing. Performance is key to Symbolics.jl. If you find any performance issues, please file an issue.
  • build_function: lambdify is “fine” for some people, but if you're building a super fast MPI-enabled Julia/C/Fortran simulation code, having a function that hits the Python interpreter is less than optimal. By default, build_function builds fast JIT-compiled functions due to being in Julia. However, it has support for things like static arrays, non-allocating functions via mutation, fast functions on sparse matrices and arrays of arrays, etc.: all core details of doing high performance computing.
  • Parallelism: Symbolics.jl has pervasive parallelism. The symbolic simplification via SymbolicUtils.jl has built-in parallelism, Symbolics.jl builds functions that parallelize across threads. Symbolics.jl is compatible with GPU libraries like CUDA.jl.
  • Extensible: Symbolics.jl and its underlying tools are written in pure Julia. Want to add new or better simplification rules? Add some Julia code! Need to add new derivatives? Add some Julia code! You get the picture. Breaking down these barriers makes it easier for the user to tailor the program to their needs and accelerates the development of the library.
  • Deep integration with the Julia ecosystem: Symbolics.jl's integration with neural networks is not the only thing that's deep. Symbolics.jl is built with the same philosophy as other SciML packages, eschewing “monorepos” for a distributed development approach that ties together the work of many developers. The differentiation parts utilize tools from automatic differentiation libraries, all linear algebra functionality comes from tracing Julia Base itself, symbolic rewriting (simplification and substitution) comes from SymbolicUtils.jl, parallelism comes from Julia Base libraries, Dagger.jl, etc. SciML Tools like DataDrivenDiffEq.jl can reconstruct symbolic expressions from neural networks and data, while NeuralPDE.jl can automatically solve partial differential equations from symbolic descriptions using physics-informed neural networks. The list keeps going. All told, by design Symbolics.jl's development moves fast because it's effectively using the work of hundreds of Julia developers, allowing it to grow fast.
  • While Symbolics.jl has many features missing from SymPy, it does not superset SymPy's functionality. For a list of missing features, see this issue.
+Comparison Against SymPy · Symbolics.jl

Comparison of Julia's Symbolics.jl vs SymPy for Symbolic Computation

Symbolics.jl is a symbolic modeling language for Julia, built in Julia. Its goal is very different from Sympy: it was made to support symbolic-numerics, the combination of symbolic computing with numerical methods to allow for extreme performance computing that would not be possible without modifying the model. Because of this, Symbolics.jl excels in many areas due to purposeful design decisions:

  • Performance: Symbolics.jl is built in Julia, whereas SymPy was built in Python. Thus, the performance bar for Symbolics.jl is much higher. Symbolics.jl started because SymPy was far too slow and SymEngine was far too inflexible for the projects they were doing. Performance is key to Symbolics.jl. If you find any performance issues, please file an issue.
  • build_function: lambdify is “fine” for some people, but if you're building a super fast MPI-enabled Julia/C/Fortran simulation code, having a function that hits the Python interpreter is less than optimal. By default, build_function builds fast JIT-compiled functions due to being in Julia. However, it has support for things like static arrays, non-allocating functions via mutation, fast functions on sparse matrices and arrays of arrays, etc.: all core details of doing high performance computing.
  • Parallelism: Symbolics.jl has pervasive parallelism. The symbolic simplification via SymbolicUtils.jl has built-in parallelism, Symbolics.jl builds functions that parallelize across threads. Symbolics.jl is compatible with GPU libraries like CUDA.jl.
  • Extensible: Symbolics.jl and its underlying tools are written in pure Julia. Want to add new or better simplification rules? Add some Julia code! Need to add new derivatives? Add some Julia code! You get the picture. Breaking down these barriers makes it easier for the user to tailor the program to their needs and accelerates the development of the library.
  • Deep integration with the Julia ecosystem: Symbolics.jl's integration with neural networks is not the only thing that's deep. Symbolics.jl is built with the same philosophy as other SciML packages, eschewing “monorepos” for a distributed development approach that ties together the work of many developers. The differentiation parts utilize tools from automatic differentiation libraries, all linear algebra functionality comes from tracing Julia Base itself, symbolic rewriting (simplification and substitution) comes from SymbolicUtils.jl, parallelism comes from Julia Base libraries, Dagger.jl, etc. SciML Tools like DataDrivenDiffEq.jl can reconstruct symbolic expressions from neural networks and data, while NeuralPDE.jl can automatically solve partial differential equations from symbolic descriptions using physics-informed neural networks. The list keeps going. All told, by design Symbolics.jl's development moves fast because it's effectively using the work of hundreds of Julia developers, allowing it to grow fast.
  • While Symbolics.jl has many features missing from SymPy, it does not superset SymPy's functionality. For a list of missing features, see this issue.
diff --git a/dev/getting_started/index.html b/dev/getting_started/index.html index 3e0f3fa2b..7faa92ebf 100644 --- a/dev/getting_started/index.html +++ b/dev/getting_started/index.html @@ -194,16 +194,16 @@ begin var"##out#236" = ˍ₋out.nzval begin - (Symbolics.var"#noop#447"())(map(fetch, (begin + (Symbolics.var"#noop#444"())(map(fetch, (begin let - task = Base.Task((Symbolics.Funcall)(RuntimeGeneratedFunctions.RuntimeGeneratedFunction{(Symbol("##out#236"), :ˍ₋arg1), Symbolics.var"#_RGF_ModTag", Symbolics.var"#_RGF_ModTag", (0xa2f7d5c0, 0x91e74e4e, 0xd03beeee, 0xda6a22d0, 0x4ed3657f), Nothing}(nothing), (var"##out#236", ˍ₋arg1))) + task = Base.Task((Symbolics.Funcall)(RuntimeGeneratedFunctions.RuntimeGeneratedFunction{(Symbol("##out#236"), :ˍ₋arg1), Symbolics.var"#_RGF_ModTag", Symbolics.var"#_RGF_ModTag", (0x95031f7e, 0x4f74d35b, 0x13f815a1, 0xb352fe3f, 0x855fe682), Nothing}(nothing), (var"##out#236", ˍ₋arg1))) task.sticky = false Base.schedule(task) task end end, begin let - task = Base.Task((Symbolics.Funcall)(RuntimeGeneratedFunctions.RuntimeGeneratedFunction{(Symbol("##out#236"), :ˍ₋arg1), Symbolics.var"#_RGF_ModTag", Symbolics.var"#_RGF_ModTag", (0xeca38124, 0x621b70be, 0xfbd2fc1f, 0x8e6c7cd4, 0x8cfb4c20), Nothing}(nothing), (var"##out#236", ˍ₋arg1))) + task = Base.Task((Symbolics.Funcall)(RuntimeGeneratedFunctions.RuntimeGeneratedFunction{(Symbol("##out#236"), :ˍ₋arg1), Symbolics.var"#_RGF_ModTag", Symbolics.var"#_RGF_ModTag", (0x4922dc0e, 0x48e8de0c, 0x907e6564, 0x5fce431d, 0xfaab2500), Nothing}(nothing), (var"##out#236", ˍ₋arg1))) task.sticky = false Base.schedule(task) task @@ -233,9 +233,9 @@ out = sparse(rows, cols, zeros(length(cols)), size(sj)...) # pre-allocate, and correct structure myf = eval(last(f_expr)) myf(out, rand(N)) # note that out matches the sparsity structure of sj -out
20×10 SparseArrays.SparseMatrixCSC{Float64, Int64} with 18 stored entries:
-⎡⠀⠈⠀⠀⠀⎤
-⎢⠰⡠⠆⠁⠀⎥
-⎢⠀⠀⠀⠃⠀⎥
-⎢⠀⠆⠀⠂⠀⎥
-⎣⢀⠀⠐⠁⢐⎦
+out
20×10 SparseArrays.SparseMatrixCSC{Float64, Int64} with 28 stored entries:
+⎡⠠⠐⠀⠕⢀⎤
+⎢⠀⠀⠈⠀⠠⎥
+⎢⡁⠑⠄⣀⢌⎥
+⎢⢂⠀⠚⢅⠐⎥
+⎣⠀⠀⠐⠀⠀⎦
diff --git a/dev/index.html b/dev/index.html index ddd0ccbc0..72cf90494 100644 --- a/dev/index.html +++ b/dev/index.html @@ -13,7 +13,7 @@ ⌅ [2edaba10] Nemo v0.47.5 [1dea7af3] OrdinaryDiffEq v6.90.1 [91a5bcdd] Plots v1.40.9 - [90137ffa] StaticArrays v1.9.8 + [90137ffa] StaticArrays v1.9.9 [d1185830] SymbolicUtils v3.7.2 [0c5d862f] Symbolics v6.22.0 `~/work/Symbolics.jl/Symbolics.jl` Info Packages marked with ⌅ have new versions available but compatibility constraints restrict them from upgrading. To see why use `status --outdated`
and using this machine and Julia version.
Julia Version 1.11.2
@@ -72,7 +72,7 @@
   [163ba53b] DiffResults v1.1.0
   [b552c78f] DiffRules v1.15.1
   [a0c0ee7d] DifferentiationInterface v0.6.27
-  [31c24e10] Distributions v0.25.114
+  [31c24e10] Distributions v0.25.115
   [ffbed154] DocStringExtensions v0.9.3
   [e30172f5] Documenter v1.8.0
   [5b8099bc] DomainSets v0.7.14
@@ -86,6 +86,7 @@
   [c87230d0] FFMPEG v0.4.2
   [7034ab61] FastBroadcast v0.3.5
   [9aa1b823] FastClosures v0.3.2
+  [442a2c76] FastGaussQuadrature v1.0.2
   [29a986be] FastLapackInterface v2.0.4
   [a4df4552] FastPower v1.1.1
   [1a297f60] FillArrays v1.13.0
@@ -102,7 +103,7 @@
   [86223c79] Graphs v1.12.0
   [42e2da0e] Grisu v1.0.2
   [0b43b601] Groebner v0.8.2
-  [cd3eb016] HTTP v1.10.14
+  [cd3eb016] HTTP v1.10.15
   [3e5b6fbb] HostCPUFeatures v0.1.17
   [34004b35] HypergeometricFunctions v0.3.25
   [b5f81e59] IOCapture v0.2.5
@@ -148,36 +149,36 @@
   [5959db7a] NonlinearSolveFirstOrder v1.2.0
   [9a2c21bd] NonlinearSolveQuasiNewton v1.1.0
   [26075421] NonlinearSolveSpectralMethods v1.1.0
-  [6fe1bfb0] OffsetArrays v1.14.2
+  [6fe1bfb0] OffsetArrays v1.15.0
   [4d8831e6] OpenSSL v1.4.3
   [bac558e1] OrderedCollections v1.7.0
   [1dea7af3] OrdinaryDiffEq v6.90.1
   [89bda076] OrdinaryDiffEqAdamsBashforthMoulton v1.1.0
-  [6ad6398a] OrdinaryDiffEqBDF v1.1.2
-  [bbf590c4] OrdinaryDiffEqCore v1.13.0
+  [6ad6398a] OrdinaryDiffEqBDF v1.2.0
+  [bbf590c4] OrdinaryDiffEqCore v1.14.1
   [50262376] OrdinaryDiffEqDefault v1.1.0
-  [4302a76b] OrdinaryDiffEqDifferentiation v1.2.0
+  [4302a76b] OrdinaryDiffEqDifferentiation v1.3.0
   [9286f039] OrdinaryDiffEqExplicitRK v1.1.0
-  [e0540318] OrdinaryDiffEqExponentialRK v1.1.0
-  [becaefa8] OrdinaryDiffEqExtrapolation v1.2.1
-  [5960d6e9] OrdinaryDiffEqFIRK v1.5.0
+  [e0540318] OrdinaryDiffEqExponentialRK v1.2.0
+  [becaefa8] OrdinaryDiffEqExtrapolation v1.3.0
+  [5960d6e9] OrdinaryDiffEqFIRK v1.6.0
   [101fe9f7] OrdinaryDiffEqFeagin v1.1.0
   [d3585ca7] OrdinaryDiffEqFunctionMap v1.1.1
   [d28bc4f8] OrdinaryDiffEqHighOrderRK v1.1.0
-  [9f002381] OrdinaryDiffEqIMEXMultistep v1.1.0
+  [9f002381] OrdinaryDiffEqIMEXMultistep v1.2.0
   [521117fe] OrdinaryDiffEqLinear v1.1.0
   [1344f307] OrdinaryDiffEqLowOrderRK v1.2.0
   [b0944070] OrdinaryDiffEqLowStorageRK v1.2.1
   [127b3ac7] OrdinaryDiffEqNonlinearSolve v1.3.0
   [c9986a66] OrdinaryDiffEqNordsieck v1.1.0
-  [5dd0a6cf] OrdinaryDiffEqPDIRK v1.1.0
+  [5dd0a6cf] OrdinaryDiffEqPDIRK v1.2.0
   [5b33eab2] OrdinaryDiffEqPRK v1.1.0
   [04162be5] OrdinaryDiffEqQPRK v1.1.0
   [af6ede74] OrdinaryDiffEqRKN v1.1.0
-  [43230ef6] OrdinaryDiffEqRosenbrock v1.3.1
-  [2d112036] OrdinaryDiffEqSDIRK v1.1.0
+  [43230ef6] OrdinaryDiffEqRosenbrock v1.4.0
+  [2d112036] OrdinaryDiffEqSDIRK v1.2.0
   [669c94d9] OrdinaryDiffEqSSPRK v1.2.0
-  [e3e12d00] OrdinaryDiffEqStabilizedIRK v1.1.0
+  [e3e12d00] OrdinaryDiffEqStabilizedIRK v1.2.0
   [358294b1] OrdinaryDiffEqStabilizedRK v1.1.0
   [fa646aed] OrdinaryDiffEqSymplecticRK v1.1.0
   [b1df2697] OrdinaryDiffEqTsit5 v1.1.0
@@ -211,7 +212,7 @@
   [7e49a35a] RuntimeGeneratedFunctions v0.5.13
   [94e857df] SIMDTypes v0.1.0
   [476501e8] SLEEFPirates v0.6.43
-  [0bca4576] SciMLBase v2.68.1
+  [0bca4576] SciMLBase v2.69.0
   [19f34311] SciMLJacobianOperators v0.1.1
   [c0aeaf25] SciMLOperators v0.3.12
   [53ae85a6] SciMLStructures v1.6.1
@@ -230,7 +231,7 @@
   [860ef19b] StableRNGs v1.0.2
   [aedffcd0] Static v1.1.1
   [0d7ed370] StaticArrayInterface v1.8.0
-  [90137ffa] StaticArrays v1.9.8
+  [90137ffa] StaticArrays v1.9.9
   [1e83bf80] StaticArraysCore v1.4.3
   [10745b16] Statistics v1.11.1
   [82ae8749] StatsAPI v1.7.0
@@ -266,27 +267,27 @@
   [2702e6a9] EpollShim_jll v0.0.20230411+1
   [2e619515] Expat_jll v2.6.4+1
 ⌅ [b22a6f82] FFMPEG_jll v4.4.4+1
-  [e134572f] FLINT_jll v300.100.300+0
+  [e134572f] FLINT_jll v300.100.301+0
   [a3f928ae] Fontconfig_jll v2.15.0+0
   [d7e528f0] FreeType2_jll v2.13.3+1
-  [559328eb] FriBidi_jll v1.0.14+0
-  [0656b61e] GLFW_jll v3.4.0+1
-  [d2c73de3] GR_jll v0.73.9+0
+  [559328eb] FriBidi_jll v1.0.16+0
+  [0656b61e] GLFW_jll v3.4.0+2
+⌅ [d2c73de3] GR_jll v0.73.9+0
   [78b55507] Gettext_jll v0.21.0+0
   [f8c6e375] Git_jll v2.47.1+0
-  [7746bdde] Glib_jll v2.82.2+1
+  [7746bdde] Glib_jll v2.82.4+0
   [3b182d85] Graphite2_jll v1.3.14+1
   [2e76f6c2] HarfBuzz_jll v8.5.0+0
   [1d5cc7b8] IntelOpenMP_jll v2024.2.1+0
-  [aacddb02] JpegTurbo_jll v3.0.4+0
+  [aacddb02] JpegTurbo_jll v3.1.0+0
   [c1c5ebd0] LAME_jll v3.100.2+0
-  [88015f11] LERC_jll v4.0.0+0
+  [88015f11] LERC_jll v4.0.0+1
   [1d63c593] LLVMOpenMP_jll v18.1.7+0
   [dd4b983a] LZO_jll v2.10.2+1
 ⌅ [e9f186c6] Libffi_jll v3.2.2+2
   [d4300ac3] Libgcrypt_jll v1.11.0+0
   [7e76a0d4] Libglvnd_jll v1.7.0+0
-  [7add5ba3] Libgpg_error_jll v1.50.0+0
+  [7add5ba3] Libgpg_error_jll v1.51.0+0
   [94ce4f54] Libiconv_jll v1.17.0+1
   [4b2f31a3] Libmount_jll v2.40.2+0
   [89763e89] Libtiff_jll v4.7.0+0
@@ -297,7 +298,7 @@
   [458c3c95] OpenSSL_jll v3.0.15+1
   [efe28fd5] OpenSpecFun_jll v0.5.5+0
   [91d4177d] Opus_jll v1.3.3+0
-  [36c8627f] Pango_jll v1.54.1+0
+  [36c8627f] Pango_jll v1.55.5+0
 ⌅ [30392449] Pixman_jll v0.43.4+0
   [c0090381] Qt6Base_jll v6.7.1+1
   [629bc702] Qt6Declarative_jll v6.7.1+2
@@ -305,7 +306,7 @@
   [e99dba38] Qt6Wayland_jll v6.7.1+1
   [f50d1b31] Rmath_jll v0.5.1+0
   [a44049a8] Vulkan_Loader_jll v1.3.243+0
-  [a2964d1f] Wayland_jll v1.21.0+1
+  [a2964d1f] Wayland_jll v1.21.0+2
   [2381bf8a] Wayland_protocols_jll v1.31.0+0
   [02c8fc9c] XML2_jll v2.13.5+0
   [aed1982a] XSLT_jll v1.1.42+0
@@ -314,11 +315,11 @@
   [c834827a] Xorg_libSM_jll v1.2.4+0
   [4f6342f7] Xorg_libX11_jll v1.8.6+1
   [0c0b7dd1] Xorg_libXau_jll v1.0.11+1
-  [935fb764] Xorg_libXcursor_jll v1.2.0+4
+  [935fb764] Xorg_libXcursor_jll v1.2.3+0
   [a3789734] Xorg_libXdmcp_jll v1.1.4+1
   [1082639a] Xorg_libXext_jll v1.3.6+1
-  [d091e8ba] Xorg_libXfixes_jll v5.0.3+4
-  [a51aa0fd] Xorg_libXi_jll v1.7.10+4
+  [d091e8ba] Xorg_libXfixes_jll v6.0.0+0
+  [a51aa0fd] Xorg_libXi_jll v1.8.2+0
   [d1454406] Xorg_libXinerama_jll v1.1.5+0
   [ec84b674] Xorg_libXrandr_jll v1.5.4+0
   [ea2f1a96] Xorg_libXrender_jll v0.9.11+1
@@ -350,7 +351,7 @@
   [1317d2d5] oneTBB_jll v2021.12.0+0
 ⌅ [1270edf5] x264_jll v2021.5.5+0
 ⌅ [dfaa095f] x265_jll v3.5.0+0
-  [d8fb68d0] xkbcommon_jll v1.4.1+1
+  [d8fb68d0] xkbcommon_jll v1.4.1+2
   [0dad84c5] ArgTools v1.1.2
   [56f22d72] Artifacts v1.11.0
   [2a0f44e3] Base64 v1.11.0
@@ -402,4 +403,4 @@
   [8e850b90] libblastrampoline_jll v5.11.0+0
   [8e850ede] nghttp2_jll v1.59.0+0
   [3f19e933] p7zip_jll v17.4.0+2
-Info Packages marked with ⌅ have new versions available but compatibility constraints restrict them from upgrading. To see why use `status --outdated -m`

You can also download the manifest file and the project file.

+Info Packages marked with ⌅ have new versions available but compatibility constraints restrict them from upgrading. To see why use `status --outdated -m`

You can also download the manifest file and the project file.

diff --git a/dev/manual/arrays/index.html b/dev/manual/arrays/index.html index 3f5a25275..7fd38d2b3 100644 --- a/dev/manual/arrays/index.html +++ b/dev/manual/arrays/index.html @@ -64,4 +64,4 @@ A_{2,1} A_{3,1} + A_{2,2} A_{3,2} + A_{2,3} A_{3,3} \end{equation} \]

@syms i::Int j::Int
-Symbolics.scalarize(AAt[i,j])

In general, any scalar expression which is derived from array expressions can be scalarized.

#sum(A[:,1]) + sum(A[2,:])#latexify not working
Symbolics.scalarize(sum(A[:,1]) + sum(A[2,:]))
+Symbolics.scalarize(AAt[i,j])

In general, any scalar expression which is derived from array expressions can be scalarized.

#sum(A[:,1]) + sum(A[2,:])#latexify not working
Symbolics.scalarize(sum(A[:,1]) + sum(A[2,:]))
diff --git a/dev/manual/build_function/index.html b/dev/manual/build_function/index.html index da302084d..1b6bffeaf 100644 --- a/dev/manual/build_function/index.html +++ b/dev/manual/build_function/index.html @@ -3,7 +3,7 @@ expression = Val{true}, target = JuliaTarget(), parallel=nothing, - kwargs...)

Generates a numerically-usable function from a Symbolics Num.

Arguments:

Keyword Arguments:

Note that not all build targets support the full compilation interface. Check the individual target documentation for details.

source

Target-Specific Definitions

Symbolics._build_functionMethod
_build_function(target::JuliaTarget, rhss::AbstractArray, args...;
+               kwargs...)

Generates a numerically-usable function from a Symbolics Num.

Arguments:

  • ex: The Num to compile
  • args: The arguments of the function
  • expression: Whether to generate code or whether to generate the compiled form. By default, expression = Val{true}, which means that the code for the function is returned. If Val{false}, then the returned value is compiled.

Keyword Arguments:

  • target: The output target of the compilation process. Possible options are:
    • JuliaTarget: Generates a Julia function
    • CTarget: Generates a C function
    • StanTarget: Generates a function for compiling with the Stan probabilistic programming language
    • MATLABTarget: Generates an anonymous function for use in MATLAB and Octave environments
  • parallel: The kind of parallelism to use in the generated function. Defaults to SerialForm(), i.e. no parallelism, if ex is a single expression or an array containing <= 1500 non-zero expressions. If ex is an array of > 1500 non-zero expressions, then ShardedForm(80, 4) is used. See below for more on ShardedForm. Note that the parallel forms are not exported and thus need to be chosen like Symbolics.SerialForm(). The choices are:
    • SerialForm(): Serial execution.
    • ShardedForm(cutoff, ncalls): splits the output function into sub-functions which contain at most cutoff number of output rhss. These sub-functions are called by the top-level function that buildfunction returns. This helps in reducing the compile time of the generated function.
    • MultithreadedForm(): Multithreaded execution with a static split, evenly splitting the number of expressions per thread.
  • fname: Used by some targets for the name of the function in the target space.

Note that not all build targets support the full compilation interface. Check the individual target documentation for details.

source

Target-Specific Definitions

Symbolics._build_functionMethod
_build_function(target::JuliaTarget, rhss::AbstractArray, args...;
                    conv=toexpr,
                    expression = Val{true},
                    expression_module = @__MODULE__(),
@@ -26,14 +26,14 @@
                 convert_oop = true, force_SA = false,
                 skipzeros = outputidxs===nothing,
                 fillzeros = skipzeros && !(typeof(rhss)<:SparseMatrixCSC),
-                parallel=SerialForm(), kwargs...)

Generates a Julia function which can then be utilized for further evaluations. If expression=Val{false}, the return is a Julia function which utilizes RuntimeGeneratedFunctions.jl to be free of world-age issues.

If the rhss is a scalar, the generated function is a function with a scalar output. Otherwise, if it's an AbstractArray, the output is two functions, one for out-of-place AbstractArray output and a second which is a mutating function. The outputted functions match the given argument order, i.e., f(u,p,args...) for the out-of-place and scalar functions and f!(du,u,p,args..) for the in-place version.

Special Keyword Arguments:

  • parallel: The kind of parallelism to use in the generated function. Defaults to SerialForm(), i.e. no parallelism. Note that the parallel forms are not exported and thus need to be chosen like Symbolics.SerialForm(). The choices are:
    • SerialForm(): Serial execution.
    • ShardedForm(cutoff, ncalls): splits the output function into sub-functions which contain at most cutoff number of output rhss. These sub-functions are called by the top-level function that buildfunction returns.
    • MultithreadedForm(): Multithreaded execution with a static split, evenly splitting the number of expressions per thread.
  • conv: The conversion function of symbolic types to Expr. By default, this uses the toexpr function.
  • checkbounds: For whether to enable bounds checking inside the generated function. Defaults to false, meaning that @inbounds is applied.
  • linenumbers: Determines whether the generated function expression retains the line numbers. Defaults to true.
  • convert_oop: Determines whether the OOP version should try to convert the output to match the type of the first input. This is useful for cases like LabelledArrays or other array types that carry extra information. Defaults to true.
  • force_SA: Forces the output of the OOP version to be a StaticArray. Defaults to false, and outputs a static array when the first argument is a static array.
  • skipzeros: Whether to skip filling zeros in the in-place version if the filling function is 0.
  • fillzeros: Whether to perform fill(out,0) before the calculations to ensure safety with skipzeros.
source
Symbolics._build_functionMethod

Build function target: CTarget

_build_function(target::CTarget, eqs::Array{<:Equation}, args...;
+                parallel=SerialForm(), kwargs...)

Generates a Julia function which can then be utilized for further evaluations. If expression=Val{false}, the return is a Julia function which utilizes RuntimeGeneratedFunctions.jl to be free of world-age issues.

If the rhss is a scalar, the generated function is a function with a scalar output. Otherwise, if it's an AbstractArray, the output is two functions, one for out-of-place AbstractArray output and a second which is a mutating function. The outputted functions match the given argument order, i.e., f(u,p,args...) for the out-of-place and scalar functions and f!(du,u,p,args..) for the in-place version.

Special Keyword Arguments:

  • parallel: The kind of parallelism to use in the generated function. Defaults to SerialForm(), i.e. no parallelism. Note that the parallel forms are not exported and thus need to be chosen like Symbolics.SerialForm(). The choices are:
    • SerialForm(): Serial execution.
    • ShardedForm(cutoff, ncalls): splits the output function into sub-functions which contain at most cutoff number of output rhss. These sub-functions are called by the top-level function that buildfunction returns.
    • MultithreadedForm(): Multithreaded execution with a static split, evenly splitting the number of expressions per thread.
  • conv: The conversion function of symbolic types to Expr. By default, this uses the toexpr function.
  • checkbounds: For whether to enable bounds checking inside the generated function. Defaults to false, meaning that @inbounds is applied.
  • linenumbers: Determines whether the generated function expression retains the line numbers. Defaults to true.
  • convert_oop: Determines whether the OOP version should try to convert the output to match the type of the first input. This is useful for cases like LabelledArrays or other array types that carry extra information. Defaults to true.
  • force_SA: Forces the output of the OOP version to be a StaticArray. Defaults to false, and outputs a static array when the first argument is a static array.
  • skipzeros: Whether to skip filling zeros in the in-place version if the filling function is 0.
  • fillzeros: Whether to perform fill(out,0) before the calculations to ensure safety with skipzeros.
source
Symbolics._build_functionMethod

Build function target: CTarget

_build_function(target::CTarget, eqs::Array{<:Equation}, args...;
                 conv = toexpr, expression = Val{true},
                 fname = :diffeqf,
                 lhsname=:du,rhsnames=[Symbol("RHS$i") for i in 1:length(args)],
-                libpath=tempname(), compiler=:gcc)

This builds an in-place C function. Only works on arrays of equations. If expression == Val{false}, then this builds a function in C, compiles it, and returns a lambda to that compiled function. These special keyword arguments control the compilation:

  • libpath: the path to store the binary. Defaults to a temporary path.
  • compiler: which C compiler to use. Defaults to :gcc, which is currently the only available option.
source
Symbolics._build_functionMethod

Build function target: StanTarget

_build_function(target::StanTarget, eqs::Array{<:Equation}, vs, ps, iv;
+                libpath=tempname(), compiler=:gcc)

This builds an in-place C function. Only works on arrays of equations. If expression == Val{false}, then this builds a function in C, compiles it, and returns a lambda to that compiled function. These special keyword arguments control the compilation:

  • libpath: the path to store the binary. Defaults to a temporary path.
  • compiler: which C compiler to use. Defaults to :gcc, which is currently the only available option.
source
Symbolics._build_functionMethod

Build function target: StanTarget

_build_function(target::StanTarget, eqs::Array{<:Equation}, vs, ps, iv;
                 conv = toexpr, expression = Val{true},
                 fname = :diffeqf, lhsname=:internal_var___du,
-                rhsnames=[:internal_var___u,:internal_var___p,:internal_var___t])

This builds an in-place Stan function compatible with the Stan differential equation solvers. Unlike other build targets, this one requires (vs, ps, iv) as the function arguments. Only allowed on arrays of equations.

source
Symbolics._build_functionMethod

Build function target: MATLABTarget

_build_function(target::MATLABTarget, eqs::Array{<:Equation}, args...;
+                rhsnames=[:internal_var___u,:internal_var___p,:internal_var___t])

This builds an in-place Stan function compatible with the Stan differential equation solvers. Unlike other build targets, this one requires (vs, ps, iv) as the function arguments. Only allowed on arrays of equations.

source
Symbolics._build_functionMethod

Build function target: MATLABTarget

_build_function(target::MATLABTarget, eqs::Array{<:Equation}, args...;
                 conv = toexpr, expression = Val{true},
                 lhsname=:internal_var___du,
-                rhsnames=[:internal_var___u,:internal_var___p,:internal_var___t])

This builds an out of place anonymous function @(t,rhsnames[1]) to be used in MATLAB. Compatible with the MATLAB differential equation solvers. Only allowed on expressions, and arrays of expressions.

source

Limitations

build_function

+ rhsnames=[:internal_var___u,:internal_var___p,:internal_var___t])

This builds an out of place anonymous function @(t,rhsnames[1]) to be used in MATLAB. Compatible with the MATLAB differential equation solvers. Only allowed on expressions, and arrays of expressions.

source

Limitations

build_function

diff --git a/dev/manual/derivatives/index.html b/dev/manual/derivatives/index.html index 5eea87a99..f00d2f8ec 100644 --- a/dev/manual/derivatives/index.html +++ b/dev/manual/derivatives/index.html @@ -13,9 +13,9 @@ (D'~x(t)) ∘ (D'~y(t)) julia> D3 = Differential(x)^3 # 3rd order differential operator -(D'~x(t)) ∘ (D'~x(t)) ∘ (D'~x(t))source
Symbolics.expand_derivativesFunction
expand_derivatives(O; ...)
-expand_derivatives(O, simplify; occurrences)
-

Expands derivatives within a symbolic expression O.

This function recursively traverses a symbolic expression, applying the chain rule and other derivative rules to expand any derivatives it encounters.

Arguments

  • O::Symbolic: The symbolic expression to expand.
  • simplify::Bool=false: Whether to simplify the resulting expression using SymbolicUtils.simplify.
  • occurrences=nothing: Information about the occurrences of the independent variable in the argument of the derivative. This is used internally for optimization purposes.

Examples

julia> @variables x y z k;
+(D'~x(t)) ∘ (D'~x(t)) ∘ (D'~x(t))
source
Symbolics.expand_derivativesFunction
expand_derivatives(O)
+expand_derivatives(O, simplify)
+

Expands derivatives within a symbolic expression O.

This function recursively traverses a symbolic expression, applying the chain rule and other derivative rules to expand any derivatives it encounters.

Arguments

  • O::Symbolic: The symbolic expression to expand.
  • simplify::Bool=false: Whether to simplify the resulting expression using SymbolicUtils.simplify.

Examples

julia> @variables x y z k;
 
 julia> f = k*(abs(x-y)/y-z)^2
 k*((abs(x - y) / y - z)^2)
@@ -24,15 +24,15 @@
 (::Differential) (generic function with 2 methods)
 
 julia> dfx = expand_derivatives(Dx(f))
-(k*((2abs(x - y)) / y - 2z)*IfElse.ifelse(signbit(x - y), -1, 1)) / y
source
Missing docstring.

Missing docstring for is_derivative. Check Documenter's build log for details.

Note

For symbolic differentiation, all registered functions in the symbolic expression need a registered derivative. For more information, see the function registration page.

High-Level Differentiation Functions

The following functions are not exported and thus must be accessed in a namespaced way, i.e. Symbolics.jacobian.

Symbolics.derivativeFunction
derivative(O, var; simplify)
-

A helper function for computing the derivative of the expression O with respect to var.

source
Symbolics.jacobianFunction
jacobian(ops, vars; simplify, scalarize)
-

A helper function for computing the Jacobian of an array of expressions with respect to an array of variable expressions.

source
Symbolics.sparsejacobianFunction
sparsejacobian(ops, vars; simplify)
-

A helper function for computing the sparse Jacobian of an array of expressions with respect to an array of variable expressions.

source
Symbolics.sparsejacobian_valsFunction
sparsejacobian_vals(ops, vars, I, J; simplify)
-

A helper function for computing the values of the sparse Jacobian of an array of expressions with respect to an array of variable expressions given the sparsity structure.

source
Symbolics.gradientFunction
gradient(O, vars; simplify)
-

A helper function for computing the gradient of the expression O with respect to an array of variable expressions.

source
Symbolics.hessianFunction
hessian(O, vars; simplify)
-

A helper function for computing the Hessian of the expression O with respect to an array of variable expressions.

source
Symbolics.sparsehessianFunction
sparsehessian(op, vars; simplify, full)
-

A helper function for computing the sparse Hessian of an expression with respect to an array of variable expressions.

source
Symbolics.sparsehessian_valsFunction
sparsehessian_vals(op, vars, I, J; simplify)
-

A helper function for computing the values of the sparse Hessian of an expression with respect to an array of variable expressions given the sparsity structure.

source

Adding Analytical Derivatives

There are many derivatives pre-defined by DiffRules.jl. For example,

using Symbolics
+(k*((2abs(x - y)) / y - 2z)*IfElse.ifelse(signbit(x - y), -1, 1)) / y
source
Missing docstring.

Missing docstring for is_derivative. Check Documenter's build log for details.

Note

For symbolic differentiation, all registered functions in the symbolic expression need a registered derivative. For more information, see the function registration page.

High-Level Differentiation Functions

The following functions are not exported and thus must be accessed in a namespaced way, i.e. Symbolics.jacobian.

Symbolics.derivativeFunction
derivative(O, var; simplify)
+

A helper function for computing the derivative of the expression O with respect to var.

source
Symbolics.jacobianFunction
jacobian(ops, vars; simplify, scalarize)
+

A helper function for computing the Jacobian of an array of expressions with respect to an array of variable expressions.

source
Symbolics.sparsejacobianFunction
sparsejacobian(ops, vars; simplify)
+

A helper function for computing the sparse Jacobian of an array of expressions with respect to an array of variable expressions.

source
Symbolics.sparsejacobian_valsFunction
sparsejacobian_vals(ops, vars, I, J; simplify)
+

A helper function for computing the values of the sparse Jacobian of an array of expressions with respect to an array of variable expressions given the sparsity structure.

source
Symbolics.gradientFunction
gradient(O, vars; simplify)
+

A helper function for computing the gradient of the expression O with respect to an array of variable expressions.

source
Symbolics.hessianFunction
hessian(O, vars; simplify)
+

A helper function for computing the Hessian of the expression O with respect to an array of variable expressions.

source
Symbolics.sparsehessianFunction
sparsehessian(op, vars; simplify, full)
+

A helper function for computing the sparse Hessian of an expression with respect to an array of variable expressions.

source
Symbolics.sparsehessian_valsFunction
sparsehessian_vals(op, vars, I, J; simplify)
+

A helper function for computing the values of the sparse Hessian of an expression with respect to an array of variable expressions given the sparsity structure.

source

Adding Analytical Derivatives

There are many derivatives pre-defined by DiffRules.jl. For example,

using Symbolics
 @variables x y z
 f(x,y,z) = x^2 + sin(x+y) - z
f (generic function with 1 method)

f automatically has the derivatives defined via the tracing mechanism. It will do this by directly building the internals of your function and differentiating that.

However, often you may want to define your own derivatives so that way automatic Jacobian etc. calculations can utilize this information. This can allow for more succinct versions of the derivatives to be calculated to scale to larger systems. You can define derivatives for your function via the dispatch:

# `N` arguments are accepted by the relevant method of `my_function`
-Symbolics.derivative(::typeof(my_function), args::NTuple{N,Any}, ::Val{i})

where i means that it's the derivative with respect to the ith argument. args is the array of arguments, so, for example, if your function is f(x,t), then args = [x,t]. You should return an Term for the derivative of your function.

For example, sin(t)'s derivative (by t) is given by the following:

Symbolics.derivative(::typeof(sin), args::NTuple{1,Any}, ::Val{1}) = cos(args[1])
+Symbolics.derivative(::typeof(my_function), args::NTuple{N,Any}, ::Val{i})

where i means that it's the derivative with respect to the ith argument. args is the array of arguments, so, for example, if your function is f(x,t), then args = [x,t]. You should return an Term for the derivative of your function.

For example, sin(t)'s derivative (by t) is given by the following:

Symbolics.derivative(::typeof(sin), args::NTuple{1,Any}, ::Val{1}) = cos(args[1])
diff --git a/dev/manual/expression_manipulation/index.html b/dev/manual/expression_manipulation/index.html index b4d4ca2ae..9fa7a018b 100644 --- a/dev/manual/expression_manipulation/index.html +++ b/dev/manual/expression_manipulation/index.html @@ -10,7 +10,7 @@ julia> substitute(ex, Dict([x => z, sin(z) => z^2])) (z(t) + y) + (z(t) ^ 2) julia> substitute(sqrt(2x), Dict([x => 1]); fold=false) -sqrt(2)source
SymbolicUtils.simplifyFunction
simplify(x; expand=false,
+sqrt(2)
source
SymbolicUtils.simplifyFunction
simplify(x; expand=false,
             threaded=false,
             thread_subtree_cutoff=100,
             rewriter=nothing)

Simplify an expression (x) by applying rewriter until there are no changes. expand=true applies expand in the beginning of each fixpoint iteration.

By default, simplify will assume denominators are not zero and allow cancellation in fractions. Pass simplify_fractions=false to prevent this.

source

Documentation for rewriter can be found here, using the @rule macro or the @acrule macro from SymbolicUtils.jl.

Additional Manipulation Functions

Other additional manipulation functions are given below.

Symbolics.get_variablesFunction
get_variables(e, varlist = nothing; sort::Bool = false)

Return a vector of variables appearing in e, optionally restricting to variables in varlist.

Note that the returned variables are not wrapped in the Num type.

Examples

julia> @variables t x y z(t);
@@ -24,7 +24,7 @@
 julia> Symbolics.get_variables(x - y; sort = true)
 2-element Vector{SymbolicUtils.BasicSymbolic}:
  x
- y
source
Symbolics.tosymbolFunction
tosymbol(x::Union{Num,Symbolic}; states=nothing, escape=true) -> Symbol

Convert x to a symbol. states are the states of a system, and escape means if the target has escapes like val"y(t)". If escape is false, then it will only output y instead of y(t).

Examples

julia> @variables t z(t)
+ y
source
Symbolics.tosymbolFunction
tosymbol(x::Union{Num,Symbolic}; states=nothing, escape=true) -> Symbol

Convert x to a symbol. states are the states of a system, and escape means if the target has escapes like val"y(t)". If escape is false, then it will only output y instead of y(t).

Examples

julia> @variables t z(t)
 2-element Vector{Num}:
     t
  z(t)
@@ -33,13 +33,13 @@
 Symbol("z(t)")
 
 julia> Symbolics.tosymbol(z; escape=false)
-:z
source
Symbolics.diff2termFunction
diff2term(x, x_metadata::Dict{Datatype, Any}) -> Symbolic

Convert a differential variable to a Term. Note that it only takes a Term not a Num. Any upstream metadata can be passed via x_metadata

julia> @variables x t u(x, t) z(t)[1:2]; Dt = Differential(t); Dx = Differential(x);
+:z
source
Symbolics.diff2termFunction
diff2term(x, x_metadata::Dict{Datatype, Any}) -> Symbolic

Convert a differential variable to a Term. Note that it only takes a Term not a Num. Any upstream metadata can be passed via x_metadata

julia> @variables x t u(x, t) z(t)[1:2]; Dt = Differential(t); Dx = Differential(x);
 
 julia> Symbolics.diff2term(Symbolics.value(Dx(Dt(u))))
 uˍtx(x, t)
 
 julia> Symbolics.diff2term(Symbolics.value(Dt(z[1])))
-var"z(t)[1]ˍt"
source
Symbolics.degreeFunction
degree(p, sym=nothing)

Extract the degree of p with respect to sym.

Examples

julia> @variables x;
+var"z(t)[1]ˍt"
source
Symbolics.degreeFunction
degree(p, sym=nothing)

Extract the degree of p with respect to sym.

Examples

julia> @variables x;
 
 julia> Symbolics.degree(x^0)
 0
@@ -48,7 +48,7 @@
 1
 
 julia> Symbolics.degree(x^2)
-2
source
Symbolics.coeffFunction
coeff(p, sym=nothing)

Extract the coefficient of p with respect to sym. Note that p might need to be expanded and/or simplified with expand and/or simplify.

Examples

julia> @variables a x y;
+2
source
Symbolics.coeffFunction
coeff(p, sym=nothing)

Extract the coefficient of p with respect to sym. Note that p might need to be expanded and/or simplified with expand and/or simplify.

Examples

julia> @variables a x y;
 
 julia> Symbolics.coeff(2a, x)
 0
@@ -60,9 +60,9 @@
 1
 
 julia> Symbolics.coeff(2*x*y + y, x*y)
-2
source
Missing docstring.

Missing docstring for Symbolics.replace. Check Documenter's build log for details.

Base.occursinFunction
occursin(needle::Symbolic, haystack::Symbolic)

Determine whether the second argument contains the first argument. Note that this function doesn't handle associativity, commutativity, or distributivity.

source
Symbolics.filterchildrenFunction
filterchildren(c, x)

Returns all parts of x that fulfills the condition given in c. c can be a function or an expression. If it is a function, returns everything for which the function is true. If c is an expression, returns all expressions that matches it.

Examples:

@syms x
+2
source
Missing docstring.

Missing docstring for Symbolics.replace. Check Documenter's build log for details.

Base.occursinFunction
occursin(needle::Symbolic, haystack::Symbolic)

Determine whether the second argument contains the first argument. Note that this function doesn't handle associativity, commutativity, or distributivity.

source
Symbolics.filterchildrenFunction
filterchildren(c, x)

Returns all parts of x that fulfills the condition given in c. c can be a function or an expression. If it is a function, returns everything for which the function is true. If c is an expression, returns all expressions that matches it.

Examples:

@syms x
 Symbolics.filterchildren(x, log(x) + x + 1)

returns [x, x]

@variables t X(t)
 D = Differential(t)
-Symbolics.filterchildren(Symbolics.is_derivative, X + D(X) + D(X^2))

returns [Differential(t)(X(t)^2), Differential(t)(X(t))]

source
Symbolics.fixpoint_subFunction
fixpoint_sub(expr, dict; operator = Nothing, maxiters = 10000)

Given a symbolic expression, equation or inequality expr perform the substitutions in dict recursively until the expression does not change. Substitutions that depend on one another will thus be recursively expanded. For example, fixpoint_sub(x, Dict(x => y, y => 3)) will return 3. The operator keyword can be specified to prevent substitution of expressions inside operators of the given type. The maxiters keyword is used to limit the number of times the substitution can occur to avoid infinite loops in cases where the substitutions in dict are circular (e.g. [x => y, y => x]).

See also: fast_substitute.

source
Symbolics.fast_substituteFunction
fast_substitute(expr, dict; operator = Nothing)

Given a symbolic expression, equation or inequality expr perform the substitutions in dict. This only performs the substitutions once. For example, fast_substitute(x, Dict(x => y, y => 3)) will return y. The operator keyword can be specified to prevent substitution of expressions inside operators of the given type.

See also: fixpoint_sub.

source
Symbolics.symbolic_to_floatFunction
symbolic_to_float(x::Union{Num, BasicSymbolic})::Union{AbstractFloat, BasicSymbolic}

If the symbolic value is exactly equal to a number, converts the symbolic value to a floating point number. Otherwise retains the symbolic value.

Examples

symbolic_to_float((1//2 * x)/x) # 0.5
+Symbolics.filterchildren(Symbolics.is_derivative, X + D(X) + D(X^2))

returns [Differential(t)(X(t)^2), Differential(t)(X(t))]

source
Symbolics.fixpoint_subFunction
fixpoint_sub(expr, dict; operator = Nothing, maxiters = 10000)

Given a symbolic expression, equation or inequality expr perform the substitutions in dict recursively until the expression does not change. Substitutions that depend on one another will thus be recursively expanded. For example, fixpoint_sub(x, Dict(x => y, y => 3)) will return 3. The operator keyword can be specified to prevent substitution of expressions inside operators of the given type. The maxiters keyword is used to limit the number of times the substitution can occur to avoid infinite loops in cases where the substitutions in dict are circular (e.g. [x => y, y => x]).

See also: fast_substitute.

source
Symbolics.fast_substituteFunction
fast_substitute(expr, dict; operator = Nothing)

Given a symbolic expression, equation or inequality expr perform the substitutions in dict. This only performs the substitutions once. For example, fast_substitute(x, Dict(x => y, y => 3)) will return y. The operator keyword can be specified to prevent substitution of expressions inside operators of the given type.

See also: fixpoint_sub.

source
Symbolics.symbolic_to_floatFunction
symbolic_to_float(x::Union{Num, BasicSymbolic})::Union{AbstractFloat, BasicSymbolic}

If the symbolic value is exactly equal to a number, converts the symbolic value to a floating point number. Otherwise retains the symbolic value.

Examples

symbolic_to_float((1//2 * x)/x) # 0.5
 symbolic_to_float((1/2 * x)/x) # 0.5
-symbolic_to_float((1//2)*√(279//4)) # 4.175823272122517
source
+symbolic_to_float((1//2)*√(279//4)) # 4.175823272122517source diff --git a/dev/manual/faq/index.html b/dev/manual/faq/index.html index c3a149d13..99c792abb 100644 --- a/dev/manual/faq/index.html +++ b/dev/manual/faq/index.html @@ -30,4 +30,4 @@ b = only(@variables($a))

\[ \begin{equation} c \end{equation} - \]

In this example, @variables($a) created a variable named c, and set this variable to b.

+ \]

In this example, @variables($a) created a variable named c, and set this variable to b.

diff --git a/dev/manual/functions/index.html b/dev/manual/functions/index.html index fadf6014f..a73442adf 100644 --- a/dev/manual/functions/index.html +++ b/dev/manual/functions/index.html @@ -41,19 +41,19 @@ \]

Note that at this time array derivatives cannot be defined.

Registration API

Symbolics.@register_symbolicMacro
@register_symbolic(expr, define_promotion = true, Ts = [Real])

Overload appropriate methods so that Symbolics can stop tracing into the registered function. If define_promotion is true, then a promotion method in the form of

SymbolicUtils.promote_symtype(::typeof(f_registered), args...) = Real # or the annotated return type

is defined for the register function. Note that when defining multiple register overloads for one function, all the rest of the registers must set define_promotion to false except for the first one, to avoid method overwriting.

Examples

@register_symbolic foo(x, y)
 @register_symbolic foo(x, y::Bool) false # do not overload a duplicate promotion rule
 @register_symbolic goo(x, y::Int) # `y` is not overloaded to take symbolic objects
-@register_symbolic hoo(x, y)::Int # `hoo` returns `Int`

See @register_array_symbolic to register functions which return arrays.

source
Symbolics.@register_array_symbolicMacro
@register_array_symbolic(expr, define_promotion = true)

Example:

# Let's say vandermonde takes an n-vector and returns an n x n matrix
+@register_symbolic hoo(x, y)::Int # `hoo` returns `Int`

See @register_array_symbolic to register functions which return arrays.

source
Symbolics.@register_array_symbolicMacro
@register_array_symbolic(expr, define_promotion = true)

Example:

# Let's say vandermonde takes an n-vector and returns an n x n matrix
 @register_array_symbolic vandermonde(x::AbstractVector) begin
     size=(length(x), length(x))
     eltype=eltype(x) # optional, will default to the promoted eltypes of x
 end

You can also register calls on callable structs:

@register_array_symbolic (c::Conv)(x::AbstractMatrix) begin
     size=size(x) .- size(c.kernel) .+ 1
     eltype=promote_type(eltype(x), eltype(c))
-end

If define_promotion = true then a promotion method in the form of

SymbolicUtils.promote_symtype(::typeof(f_registered), args...) = # inferred or annotated return type

is defined for the register function. Note that when defining multiple register overloads for one function, all the rest of the registers must set define_promotion to false except for the first one, to avoid method overwriting.

source

Direct Registration API (Advanced, Experimental)

Warning

This is a lower level API which is not as stable as the macro APIs.

In some circumstances you may need to use the direct API in order to define registration on functions or types without using the macro. This is done by directly defining dispatches on symbolic objects.

A good example of this is DataInterpolations.jl's interpolations object. On an interpolation by a symbolic variable, we generate the symbolic function (the term) for the interpolation function. This looks like:

using DataInterpolations, Symbolics, SymbolicUtils
+end

If define_promotion = true then a promotion method in the form of

SymbolicUtils.promote_symtype(::typeof(f_registered), args...) = # inferred or annotated return type

is defined for the register function. Note that when defining multiple register overloads for one function, all the rest of the registers must set define_promotion to false except for the first one, to avoid method overwriting.

source

Direct Registration API (Advanced, Experimental)

Warning

This is a lower level API which is not as stable as the macro APIs.

In some circumstances you may need to use the direct API in order to define registration on functions or types without using the macro. This is done by directly defining dispatches on symbolic objects.

A good example of this is DataInterpolations.jl's interpolations object. On an interpolation by a symbolic variable, we generate the symbolic function (the term) for the interpolation function. This looks like:

using DataInterpolations, Symbolics, SymbolicUtils
 (interp::AbstractInterpolation)(t::Num) = SymbolicUtils.term(interp, unwrap(t))

In order for this to work, it is required that we define the symtype for the symbolic type inference. This is done via:

SymbolicUtils.promote_symtype(t::AbstractInterpolation, args...) = Real

Additionally a symbolic name is required:

Base.nameof(interp::AbstractInterpolation) = :Interpolation

The derivative is defined similarly to the macro case:

function Symbolics.derivative(interp::AbstractInterpolation, args::NTuple{1, Any}, ::Val{1})
     Symbolics.unwrap(derivative(interp, Symbolics.wrap(args[1])))
-end

Inverse function registration

Symbolics.jl allows defining and querying the inverses of functions.

Symbolics.inverseFunction
inverse(f)

Given a single-input single-output function f, return its inverse g. This requires that f is bijective. If inverse is defined for a function, left_inverse and right_inverse should return inverse(f). inverse(g) should also be defined to return f.

See also: left_inverse, right_inverse, @register_inverse.

source
Symbolics.left_inverseFunction
left_inverse(f)

Given a single-input single-output function f, return its left inverse g. This requires that f is injective. If left_inverse is defined for a function, right_inverse and inverse must not be defined and should error. right_inverse(g) should also be defined to return f.

See also: inverse, right_inverse, @register_inverse.

source
Symbolics.right_inverseFunction
right_inverse(f)

Given a single-input single-output function f, return its right inverse g. This requires that f is surjective. If right_inverse is defined for a function, left_inverse and inverse must not be defined and should error. left_inverse(g) should also be defined to return f.

See also inverse, left_inverse, @register_inverse.

source
Symbolics.@register_inverseMacro
@register_inverse f g
+end

Inverse function registration

Symbolics.jl allows defining and querying the inverses of functions.

Symbolics.inverseFunction
inverse(f)

Given a single-input single-output function f, return its inverse g. This requires that f is bijective. If inverse is defined for a function, left_inverse and right_inverse should return inverse(f). inverse(g) should also be defined to return f.

See also: left_inverse, right_inverse, @register_inverse.

source
Symbolics.left_inverseFunction
left_inverse(f)

Given a single-input single-output function f, return its left inverse g. This requires that f is injective. If left_inverse is defined for a function, right_inverse and inverse must not be defined and should error. right_inverse(g) should also be defined to return f.

See also: inverse, right_inverse, @register_inverse.

source
Symbolics.right_inverseFunction
right_inverse(f)

Given a single-input single-output function f, return its right inverse g. This requires that f is surjective. If right_inverse is defined for a function, left_inverse and inverse must not be defined and should error. left_inverse(g) should also be defined to return f.

See also inverse, left_inverse, @register_inverse.

source
Symbolics.@register_inverseMacro
@register_inverse f g
 @register_inverse f g left
-@register_inverse f g right

Mark f and g as inverses of each other. By default, assume that f and g are bijective. Also defines left_inverse and right_inverse to call inverse. If the third argument is left, assume that f is injective and g is its left inverse. If the third argument is right, assume that f is surjective and g is its right inverse.

source

Symbolics.jl implements inverses for standard trigonometric and logarithmic functions, as well as their variants from NaNMath. It also implements inverses of ComposedFunctions.

+@register_inverse f g right

Mark f and g as inverses of each other. By default, assume that f and g are bijective. Also defines left_inverse and right_inverse to call inverse. If the third argument is left, assume that f is injective and g is its left inverse. If the third argument is right, assume that f is surjective and g is its right inverse.

source
Symbolics.has_inverseFunction
has_inverse(_) -> Bool
+

Check if the provided function has an inverse defined via inverse. Uses hasmethod to perform the check.

source
Symbolics.has_left_inverseFunction
has_left_inverse(_) -> Bool
+

Check if the provided function has a left inverse defined via left_inverse Uses hasmethod to perform the check.

source
Symbolics.has_right_inverseFunction
has_right_inverse(_) -> Bool
+

Check if the provided function has a left inverse defined via left_inverse Uses hasmethod to perform the check.

source

Symbolics.jl implements inverses for standard trigonometric and logarithmic functions, as well as their variants from NaNMath. It also implements inverses of ComposedFunctions.

diff --git a/dev/manual/groebner/index.html b/dev/manual/groebner/index.html index 52d85f575..8de875593 100644 --- a/dev/manual/groebner/index.html +++ b/dev/manual/groebner/index.html @@ -1,2 +1,2 @@ -Groebner bases · Symbolics.jl

Groebner bases

Groebner bases use the implementation of the F4 algorithm from Groebner.jl package as its backend. We refer to the documentation of Groebner.jl, which lists some implementations details and possible use-cases of Groebner bases.

Symbolics.groebner_basisFunction
groebner_basis(polynomials)

Computes a Groebner basis of the ideal generated by the given polynomials.

This function requires a Groebner bases backend (such as Groebner.jl) to be loaded.

source
+Groebner bases · Symbolics.jl

Groebner bases

Groebner bases use the implementation of the F4 algorithm from Groebner.jl package as its backend. We refer to the documentation of Groebner.jl, which lists some implementations details and possible use-cases of Groebner bases.

Symbolics.groebner_basisFunction
groebner_basis(polynomials)

Computes a Groebner basis of the ideal generated by the given polynomials.

This function requires a Groebner bases backend (such as Groebner.jl) to be loaded.

source
diff --git a/dev/manual/io/index.html b/dev/manual/io/index.html index f8f6850fd..6cbfe9ba6 100644 --- a/dev/manual/io/index.html +++ b/dev/manual/io/index.html @@ -6,4 +6,4 @@ end ex1, ex2 = build_function(f(u),u) write("function.jl", string(ex2))
898

Now we can do something like:

g = include("function.jl")
#1 (generic function with 1 method)

and that will load the function back in. Note that this can be done to save the transformation results of Symbolics.jl so that they can be stored and used in a precompiled Julia package.

Latexification

Symbolics.jl's expressions support Latexify.jl, and thus

using Latexify
-latexify(ex)

will produce LaTeX output from Symbolics models and expressions. This works on basics like Term all the way to higher primitives like ODESystem and ReactionSystem.

+latexify(ex)

will produce LaTeX output from Symbolics models and expressions. This works on basics like Term all the way to higher primitives like ODESystem and ReactionSystem.

diff --git a/dev/manual/limits/index.html b/dev/manual/limits/index.html index 8b5e238b0..570d0ab51 100644 --- a/dev/manual/limits/index.html +++ b/dev/manual/limits/index.html @@ -1,2 +1,2 @@ -Symbolic Limits · Symbolics.jl

Symbolic Limits

Experimental symbolic limit support is provided by the limit function, documented below. See SymbolicLimits.jl for more information and implementation details.

Symbolics.limitFunction
limit(expr, var, h[, side::Symbol])

Compute the limit of expr as var approaches h.

side indicates the direction from which var approaches h. It may be one of :left, :right, or :both. If side is :both and the two sides do not align, an error is thrown. Side defaults to :both for finite h, :left for h = Inf, and :right for h = -Inf.

expr must be composed of log, exp, constants, and the rational operators +, -, *, and /. This limitation may eventually be relaxed.

Warning

Because symbolic limit computation is undecidable, this function necessarily employs heuristics and may occasionally return wrong answers. Nevertheless, please report wrong answers as issues as we aim to have heuristics that produce correct answers in all practical cases.

source
+Symbolic Limits · Symbolics.jl

Symbolic Limits

Experimental symbolic limit support is provided by the limit function, documented below. See SymbolicLimits.jl for more information and implementation details.

Symbolics.limitFunction
limit(expr, var, h[, side::Symbol])

Compute the limit of expr as var approaches h.

side indicates the direction from which var approaches h. It may be one of :left, :right, or :both. If side is :both and the two sides do not align, an error is thrown. Side defaults to :both for finite h, :left for h = Inf, and :right for h = -Inf.

expr must be composed of log, exp, constants, and the rational operators +, -, *, and /. This limitation may eventually be relaxed.

Warning

Because symbolic limit computation is undecidable, this function necessarily employs heuristics and may occasionally return wrong answers. Nevertheless, please report wrong answers as issues as we aim to have heuristics that produce correct answers in all practical cases.

source
diff --git a/dev/manual/parsing/index.html b/dev/manual/parsing/index.html index d588ac44f..0d5bd43d1 100644 --- a/dev/manual/parsing/index.html +++ b/dev/manual/parsing/index.html @@ -15,4 +15,4 @@ z ~ 2] all(isequal.(eqs,ex)) # true

Limitations

Symbolic-ness Tied to Environment Definitions

The parsing to a symbolic expression has to be able to recognize the difference between functions, numbers, and globals defined within one's Julia environment and those that are to be made symbolic. The way this functionality handles this problem is that it does not define anything as symbolic that is already defined in the chosen mod module. For example, f(x,y) will have f as non-symbolic if the function f (named f) is defined in mod, i.e. if isdefined(mod,:f) is true. When the symbol is defined, it will be replaced by its value. Notably, this means that the parsing behavior changes depending on the environment that it is applied.

For example:

parse_expr_to_symbolic(:(x - y),@__MODULE__) # x - y
 x = 2.0
-parse_expr_to_symbolic(:(x - y),@__MODULE__) # 2.0 - y

This is required to detect that standard functions like - are functions instead of symbolic symbols. For safety, one should create anonymous modules or other sub-environments to ensure no stray variables are defined.

Metadata is Blank

Because all the variables defined by the expressions are not defined with the standard @variables, there is no metadata that is or can be associated with any of the generated variables. Instead, they all have blank metadata, but are defined in the Real domain. Thus, the variables which come out of this parsing may not evaluate as equal to a symbolic variable defined elsewhere.

source
Missing docstring.

Missing docstring for @parse_expr_to_symbolic. Check Documenter's build log for details.

+parse_expr_to_symbolic(:(x - y),@__MODULE__) # 2.0 - y

This is required to detect that standard functions like - are functions instead of symbolic symbols. For safety, one should create anonymous modules or other sub-environments to ensure no stray variables are defined.

Metadata is Blank

Because all the variables defined by the expressions are not defined with the standard @variables, there is no metadata that is or can be associated with any of the generated variables. Instead, they all have blank metadata, but are defined in the Real domain. Thus, the variables which come out of this parsing may not evaluate as equal to a symbolic variable defined elsewhere.

source
Missing docstring.

Missing docstring for @parse_expr_to_symbolic. Check Documenter's build log for details.

diff --git a/dev/manual/solver/index.html b/dev/manual/solver/index.html index 0baa713a7..d56508dd2 100644 --- a/dev/manual/solver/index.html +++ b/dev/manual/solver/index.html @@ -65,7 +65,7 @@ julia> Symbolics.symbolic_to_float.(roots) 1-element Vector{Complex{BigFloat}}: - -4.512941594732059759689023145584186058252768936052415430071569066192919491762214 + 3.428598090438030380369414618548038962770087500755160535832807433942464545729382imsource

One other symbolic solver is symbolic_linear_solve which is limited compared to symbolic_solve as it only solves linear equations.

Symbolics.symbolic_linear_solveFunction
symbolic_linear_solve(eq, var; simplify, check) -> Any
+ -4.512941594732059759689023145584186058252768936052415430071569066192919491762214 + 3.428598090438030380369414618548038962770087500755160535832807433942464545729382im
source

One other symbolic solver is symbolic_linear_solve which is limited compared to symbolic_solve as it only solves linear equations.

Symbolics.symbolic_linear_solveFunction
symbolic_linear_solve(eq, var; simplify, check) -> Any
 

Solve equation(s) eqs for a set of variables vars.

Assumes length(eqs) == length(vars)

Currently only works if all equations are linear. check if the expr is linear w.r.t vars.

Examples

julia> @variables x y
 2-element Vector{Num}:
  x
@@ -77,7 +77,7 @@
 julia> Symbolics.symbolic_linear_solve([x + y ~ 0, x - y ~ 2], [x, y])
 2-element Vector{Float64}:
   1.0
- -1.0
source

symbolic_solve only supports symbolic, i.e. non-floating point computations, and thus prefers equations where the coefficients are integer, rational, or symbolic. Floating point coefficients are transformed into rational values and BigInt values are used internally with a potential performance loss, and thus it is recommended that this functionality is only used with floating point values if necessary. In contrast, symbolic_linear_solve directly handles floating point values using standard factorizations.

More technical details and examples

Technical details

The symbolic_solve function uses 4 hidden solvers in order to solve the user's input. Its base, solve_univar, uses analytic solutions up to polynomials of degree 4 and factoring as its method for solving univariate polynomials. The function's solve_multipoly uses GCD on the input polynomials then throws passes the result to solve_univar. The function's solve_multivar uses Groebner basis and a separating form in order to create linear equations in the input variables and a single high degree equation in the separating variable [1]. Each equation resulting from the basis is then passed to solve_univar. We can see that essentially, solve_univar is the building block of symbolic_solve. If the input is not a valid polynomial and can not be solved by the algorithm above, symbolic_solve passes it to ia_solve, which attempts solving by attraction and isolation [2]. This only works when the input is a single expression and the user wants the answer in terms of a single variable. Say log(x) - a == 0 gives us [e^a].

Symbolics.solve_univarFunction
solve_univar(expression, x; dropmultiplicity=true)

This solver uses analytic solutions up to degree 4 to solve univariate polynomials. It first handles the special case of the expression being of operation ^. E.g. math (x+2)^{20}. We solve this by removing the int 20, then solving the poly math x+2 on its own. If the parameter mult of the solver is set to true, we then repeat the found roots of math x+2 twenty times before returning the results to the user.

Step 2 is filtering the expression after handling this special case, and then factoring it using factor_use_nemo. We then solve all the factors outputted using the analytic methods implemented in the function get_roots and its children.

Arguments

  • expr: Single symbolics Num or SymbolicUtils.BasicSymbolic expression. This is equated to 0 and then solved. E.g. expr = x+2, we solve x+2 = 0

  • x: Single symbolics variable

  • dropmultiplicity (optional): Print repeated roots or not?

  • strict (optional): Bool that enables/disables strict assert if input expression is a univariate polynomial or not. If strict=true and expression is not a polynomial, solve_univar throws an assertion error.

Examples

source
Missing docstring.

Missing docstring for Symbolics.solve_multivar. Check Documenter's build log for details.

Symbolics.ia_solveFunction
ia_solve(lhs, var; kwargs...)

This function attempts to solve transcendental functions by first checking the "smart" number of occurrences in the input LHS. By smart here we mean that polynomials are counted as 1 occurrence. for example x^2 + 2x is 1 occurrence of x. So we abstract all occurrences of x's as polynomials. Say: log(x+1) + x^2 is seen as log(f(x)) + g(x) so there are 2 occurrences of x. If there is only 1 occurrence of x in an input expression, isolate is called.

Isolate reverses all operations applied on the occurrence of x until we have f(x) = some constant then we can solve this using our polynomial solvers.

If more than 1 occurrence of x is found, ia_solve attempts to attract the occurrences of x in order to reduce these occurrences to 1. For example, log(x+1) + log(x-1) can be converted to log(x^2 - 1) which now could be isolated using Isolate.

attract(lhs, var) currently uses 4 techniques for attraction.

  • Log addition: log(f(x)) + log(g(x)) => log(h(x))
  • Exponential simplification: a*b^(f(x)) + c*d^(g(x)) => f(x) * log(b) - g(x) * log(d) + log(-a/c). And now this is actually 1 occurrence of x since f(x) and g(x) are just multiplied by constants not wrapped in some operation.
  • Trig simplification: this bruteforces multiple trig identities and doesn't detect them before hand.
  • Polynomialization: as a last resort, attract attempts to polynomialize the expression. Say sin(x+2)^2 + sin(x+2) + 10 is converted to X^2 + X + 10, we then solve this using our polynomial solver, and afterwards, isolate sin(x+2) = the roots found by solve for X^2 + X + 10

After attraction, we check the number of occurrences again, and if its 1, we isolate, if not, we throw an error to tell the user that this is currently unsolvable by our covered techniques.

Arguments

  • lhs: a Num/SymbolicUtils.BasicSymbolic
  • var: variable to solve for.

Keyword arguments

  • warns = true: Whether to emit warnings for unsolvable expressions.
  • complex_roots = true: Whether to consider complex roots of x ^ n ~ y, where n is an integer.
  • periodic_roots = true: If true, isolate f(x) ~ y as x ~ finv(y) + n * period where is_periodic(f) == true, finv = left_inverse(f) and period = fundamental_period(f). n is a new anonymous symbolic variable.

Examples

julia> solve(a*x^b + c, x)
+ -1.0
source

symbolic_solve only supports symbolic, i.e. non-floating point computations, and thus prefers equations where the coefficients are integer, rational, or symbolic. Floating point coefficients are transformed into rational values and BigInt values are used internally with a potential performance loss, and thus it is recommended that this functionality is only used with floating point values if necessary. In contrast, symbolic_linear_solve directly handles floating point values using standard factorizations.

More technical details and examples

Technical details

The symbolic_solve function uses 4 hidden solvers in order to solve the user's input. Its base, solve_univar, uses analytic solutions up to polynomials of degree 4 and factoring as its method for solving univariate polynomials. The function's solve_multipoly uses GCD on the input polynomials then throws passes the result to solve_univar. The function's solve_multivar uses Groebner basis and a separating form in order to create linear equations in the input variables and a single high degree equation in the separating variable [1]. Each equation resulting from the basis is then passed to solve_univar. We can see that essentially, solve_univar is the building block of symbolic_solve. If the input is not a valid polynomial and can not be solved by the algorithm above, symbolic_solve passes it to ia_solve, which attempts solving by attraction and isolation [2]. This only works when the input is a single expression and the user wants the answer in terms of a single variable. Say log(x) - a == 0 gives us [e^a].

Symbolics.solve_univarFunction
solve_univar(expression, x; dropmultiplicity=true)

This solver uses analytic solutions up to degree 4 to solve univariate polynomials. It first handles the special case of the expression being of operation ^. E.g. math (x+2)^{20}. We solve this by removing the int 20, then solving the poly math x+2 on its own. If the parameter mult of the solver is set to true, we then repeat the found roots of math x+2 twenty times before returning the results to the user.

Step 2 is filtering the expression after handling this special case, and then factoring it using factor_use_nemo. We then solve all the factors outputted using the analytic methods implemented in the function get_roots and its children.

Arguments

  • expr: Single symbolics Num or SymbolicUtils.BasicSymbolic expression. This is equated to 0 and then solved. E.g. expr = x+2, we solve x+2 = 0

  • x: Single symbolics variable

  • dropmultiplicity (optional): Print repeated roots or not?

  • strict (optional): Bool that enables/disables strict assert if input expression is a univariate polynomial or not. If strict=true and expression is not a polynomial, solve_univar throws an assertion error.

Examples

source
Missing docstring.

Missing docstring for Symbolics.solve_multivar. Check Documenter's build log for details.

Symbolics.ia_solveFunction
ia_solve(lhs, var; kwargs...)

This function attempts to solve transcendental functions by first checking the "smart" number of occurrences in the input LHS. By smart here we mean that polynomials are counted as 1 occurrence. for example x^2 + 2x is 1 occurrence of x. So we abstract all occurrences of x's as polynomials. Say: log(x+1) + x^2 is seen as log(f(x)) + g(x) so there are 2 occurrences of x. If there is only 1 occurrence of x in an input expression, isolate is called.

Isolate reverses all operations applied on the occurrence of x until we have f(x) = some constant then we can solve this using our polynomial solvers.

If more than 1 occurrence of x is found, ia_solve attempts to attract the occurrences of x in order to reduce these occurrences to 1. For example, log(x+1) + log(x-1) can be converted to log(x^2 - 1) which now could be isolated using Isolate.

attract(lhs, var) currently uses 4 techniques for attraction.

  • Log addition: log(f(x)) + log(g(x)) => log(h(x))
  • Exponential simplification: a*b^(f(x)) + c*d^(g(x)) => f(x) * log(b) - g(x) * log(d) + log(-a/c). And now this is actually 1 occurrence of x since f(x) and g(x) are just multiplied by constants not wrapped in some operation.
  • Trig simplification: this bruteforces multiple trig identities and doesn't detect them before hand.
  • Polynomialization: as a last resort, attract attempts to polynomialize the expression. Say sin(x+2)^2 + sin(x+2) + 10 is converted to X^2 + X + 10, we then solve this using our polynomial solver, and afterwards, isolate sin(x+2) = the roots found by solve for X^2 + X + 10

After attraction, we check the number of occurrences again, and if its 1, we isolate, if not, we throw an error to tell the user that this is currently unsolvable by our covered techniques.

Arguments

  • lhs: a Num/SymbolicUtils.BasicSymbolic
  • var: variable to solve for.

Keyword arguments

  • warns = true: Whether to emit warnings for unsolvable expressions.
  • complex_roots = true: Whether to consider complex roots of x ^ n ~ y, where n is an integer.
  • periodic_roots = true: If true, isolate f(x) ~ y as x ~ finv(y) + n * period where is_periodic(f) == true, finv = left_inverse(f) and period = fundamental_period(f). n is a new anonymous symbolic variable.

Examples

julia> solve(a*x^b + c, x)
 ((-c)^(1 / b)) / (a^(1 / b))
julia> solve(2^(x+1) + 5^(x+3), x)
 1-element Vector{SymbolicUtils.BasicSymbolic{Real}}:
  (-log(2) + 3log(5) - log(complex(-1))) / (log(2) - log(5))
julia> solve(log(x+1)+log(x-1), x)
@@ -91,7 +91,7 @@
 [ Info: var"##234" ϵ Ζ: e.g. 0, 1, 2...
 2-element Vector{SymbolicUtils.BasicSymbolic{Real}}:
  -2 + π*2var"##230" + asin((1//2)*(-1 + RootFinding.ssqrt(-39)))
- -2 + π*2var"##234" + asin((1//2)*(-1 - RootFinding.ssqrt(-39)))

All transcendental functions for which left_inverse is defined are supported. To enable ia_solve to handle custom transcendental functions, define an inverse or left inverse. If the function is periodic, is_periodic and fundamental_period must be defined. If the function imposes certain conditions on its input or output (for example, log requires that its input be positive) define ia_conditions!.

See also: left_inverse, inverse, is_periodic, fundamental_period, ia_conditions!.

References

source
Symbolics.ia_conditions!Function
ia_conditions!(f, lhs, rhs::Vector{Any}, conditions::Vector{Tuple})

If f is a left-invertible function, lhs and rhs[i] are univariate functions and f(lhs) ~ rhs[i] for all i in eachindex(rhss), push to conditions all the relevant conditions on lhs or rhs[i]. Each condition is of the form (sym, op) where sym is an expression involving lhs and/or rhs[i] and op is a binary relational operator. The condition op(sym, 0) is then required to be true for the equation f(lhs) ~ rhs[i] to be valid.

For example, if f = log, lhs = x and rhss = [y, z] then the condition x > 0 must be true. Thus, (lhs, >) is pushed to conditions. Similarly, if f = sqrt, rhs[i] >= 0 must be true for all i, and so (y, >=) and (z, >=) will be appended to conditions.

source
Symbolics.is_periodicFunction
is_periodic(f)

Return true if f is a single-input single-output periodic function. Return false by default. If is_periodic(f) == true, then fundamental_period(f) must also be defined.

See also: fundamental_period

source
Symbolics.fundamental_periodFunction
fundamental_period(f)

Return the fundamental period of periodic function f. Must only be called if is_periodic(f) == true.

see also: is_periodic

source

Nice examples

using Symbolics, Nemo;
+ -2 + π*2var"##234" + asin((1//2)*(-1 - RootFinding.ssqrt(-39)))

All transcendental functions for which left_inverse is defined are supported. To enable ia_solve to handle custom transcendental functions, define an inverse or left inverse. If the function is periodic, is_periodic and fundamental_period must be defined. If the function imposes certain conditions on its input or output (for example, log requires that its input be positive) define ia_conditions!.

See also: left_inverse, inverse, is_periodic, fundamental_period, ia_conditions!.

References

source
Symbolics.ia_conditions!Function
ia_conditions!(f, lhs, rhs::Vector{Any}, conditions::Vector{Tuple})

If f is a left-invertible function, lhs and rhs[i] are univariate functions and f(lhs) ~ rhs[i] for all i in eachindex(rhss), push to conditions all the relevant conditions on lhs or rhs[i]. Each condition is of the form (sym, op) where sym is an expression involving lhs and/or rhs[i] and op is a binary relational operator. The condition op(sym, 0) is then required to be true for the equation f(lhs) ~ rhs[i] to be valid.

For example, if f = log, lhs = x and rhss = [y, z] then the condition x > 0 must be true. Thus, (lhs, >) is pushed to conditions. Similarly, if f = sqrt, rhs[i] >= 0 must be true for all i, and so (y, >=) and (z, >=) will be appended to conditions.

source
Symbolics.is_periodicFunction
is_periodic(f)

Return true if f is a single-input single-output periodic function. Return false by default. If is_periodic(f) == true, then fundamental_period(f) must also be defined.

See also: fundamental_period

source
Symbolics.fundamental_periodFunction
fundamental_period(f)

Return the fundamental period of periodic function f. Must only be called if is_periodic(f) == true.

see also: is_periodic

source

Nice examples

using Symbolics, Nemo;
 @variables x;
 Symbolics.symbolic_solve(9^x + 3^x ~ 8, x)
2-element Vector{SymbolicUtils.BasicSymbolic{Real}}:
  slog(-(1//2) + (1//2)*√(33)) / slog(3)
@@ -117,4 +117,4 @@
 Out[2]= a \[Element] Reals && x > -a
 
 In[3]:= Solve[x^(x)  + 3 == 0, x]
-Out[3]= {{x -> (I \[Pi] + Log[3])/ProductLog[I \[Pi] + Log[3]]}}

References

+Out[3]= {{x -> (I \[Pi] + Log[3])/ProductLog[I \[Pi] + Log[3]]}}

References

diff --git a/dev/manual/sparsity_detection/index.html b/dev/manual/sparsity_detection/index.html index 19e2025f3..66acd679d 100644 --- a/dev/manual/sparsity_detection/index.html +++ b/dev/manual/sparsity_detection/index.html @@ -17,7 +17,7 @@ 3×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 4 stored entries: 1 ⋅ ⋅ 1 - 1 1source
jacobian_sparsity(
+ 1  1
source
jacobian_sparsity(
     f!::Function,
     output::AbstractArray,
     input::AbstractArray,
@@ -36,7 +36,7 @@
 3×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 4 stored entries:
  ⋅  1
  1  ⋅
- 1  1
source
Symbolics.hessian_sparsityFunction
hessian_sparsity(
+ 1  1
source
Symbolics.hessian_sparsityFunction
hessian_sparsity(
     expr,
     vars::AbstractVector;
     full
@@ -50,7 +50,7 @@
 julia> Symbolics.hessian_sparsity(expr, vars)
 2×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 3 stored entries:
  1  1
- 1  ⋅
source
hessian_sparsity(
+ 1  ⋅
source
hessian_sparsity(
     f::Function,
     input::AbstractVector,
     args...;
@@ -66,6 +66,6 @@
 julia> Symbolics.hessian_sparsity(f, input)
 2×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 3 stored entries:
  ⋅  1
- 1  1
source

Structure Detection

Symbolics.islinearFunction
islinear(ex, u)
-

Check if an expression is linear with respect to a list of variable expressions.

source
Symbolics.isaffineFunction
isaffine(ex, u)
-

Check if an expression is affine with respect to a list of variable expressions.

source

ADTypes.jl interface

Symbolics.SymbolicsSparsityDetectorType
SymbolicsSparsityDetector <: ADTypes.AbstractSparsityDetector

Sparsity detection algorithm based on the Symbolics.jl tracing system.

This type makes Symbolics.jl compatible with the ADTypes.jl sparsity detection framework. The following functions are implemented:

Reference

Sparsity Programming: Automated Sparsity-Aware Optimizations in Differentiable Programming, Gowda et al. (2019)

source
+ 1 1source

Structure Detection

Symbolics.islinearFunction
islinear(ex, u)
+

Check if an expression is linear with respect to a list of variable expressions.

source
Symbolics.isaffineFunction
isaffine(ex, u)
+

Check if an expression is affine with respect to a list of variable expressions.

source

ADTypes.jl interface

Symbolics.SymbolicsSparsityDetectorType
SymbolicsSparsityDetector <: ADTypes.AbstractSparsityDetector

Sparsity detection algorithm based on the Symbolics.jl tracing system.

This type makes Symbolics.jl compatible with the ADTypes.jl sparsity detection framework. The following functions are implemented:

Reference

Sparsity Programming: Automated Sparsity-Aware Optimizations in Differentiable Programming, Gowda et al. (2019)

source
diff --git a/dev/manual/taylor/index.html b/dev/manual/taylor/index.html index 960f0c43f..901e3680a 100644 --- a/dev/manual/taylor/index.html +++ b/dev/manual/taylor/index.html @@ -9,7 +9,7 @@ y[0] + (-2 + x)*y[1] + ((-2 + x)^2)*y[2] + ((-2 + x)^3)*y[3] julia> series(z, x, 2, 0:3) -z[0] + (-2 + x)*z[1] + ((-2 + x)^2)*z[2] + ((-2 + x)^3)*z[3]source
Symbolics.taylorFunction
taylor(f, x, [x0=0,] n; rationalize=true)

Calculate the n-th order term(s) in the Taylor series of f around x = x0. If rationalize, float coefficients are approximated as rational numbers (this can produce unexpected results for irrational numbers, for example).

Examples

julia> @variables x
+z[0] + (-2 + x)*z[1] + ((-2 + x)^2)*z[2] + ((-2 + x)^3)*z[3]
source
Symbolics.taylorFunction
taylor(f, x, [x0=0,] n; rationalize=true)

Calculate the n-th order term(s) in the Taylor series of f around x = x0. If rationalize, float coefficients are approximated as rational numbers (this can produce unexpected results for irrational numbers, for example).

Examples

julia> @variables x
 1-element Vector{Num}:
  x
 
@@ -23,7 +23,7 @@
 1 + (1//2)*(-1 + x) - (1//8)*((-1 + x)^2) + (1//16)*((-1 + x)^3)
 
 julia> isequal(taylor(exp(im*x), x, 0:5), taylor(exp(im*x), x, 0:5))
-true
source
Symbolics.taylor_coeffFunction
taylor_coeff(f, x[, n]; rationalize=true)

Calculate the n-th order coefficient(s) in the Taylor series of f around x = 0.

Examples

julia> @variables x y
+true
source
Symbolics.taylor_coeffFunction
taylor_coeff(f, x[, n]; rationalize=true)

Calculate the n-th order coefficient(s) in the Taylor series of f around x = 0.

Examples

julia> @variables x y
 2-element Vector{Num}:
  x
  y
@@ -32,4 +32,4 @@
 3-element Vector{Num}:
  y[0]
  y[2]
- y[4]
source
+ y[4]source diff --git a/dev/manual/types/index.html b/dev/manual/types/index.html index 9dc12db33..8c3b4ef53 100644 --- a/dev/manual/types/index.html +++ b/dev/manual/types/index.html @@ -5,4 +5,4 @@ z X[1:10,1:10] Z[1:10] - s
typeof(x)
Num
typeof(z)
Complex{Num}
typeof(X)
Symbolics.Arr{Num, 2}
typeof(Z)
Symbolics.Arr{Complex{Num}, 1}
typeof(s)
SymbolicUtils.BasicSymbolic{String}
+ s
typeof(x)
Num
typeof(z)
Complex{Num}
typeof(X)
Symbolics.Arr{Num, 2}
typeof(Z)
Symbolics.Arr{Complex{Num}, 1}
typeof(s)
SymbolicUtils.BasicSymbolic{String}
diff --git a/dev/manual/variables/index.html b/dev/manual/variables/index.html index f9faaae05..a11938ed0 100644 --- a/dev/manual/variables/index.html +++ b/dev/manual/variables/index.html @@ -28,15 +28,15 @@ (value_c(t))[1:3] julia> (t, a, b, c) -(t, :runtime_symbol_value, :value_b, :value_c)source
Symbolics.variableFunction
variable(name::Symbol, idx::Integer...; T=Real)

Create a variable with the given name along with subscripted indices with the symtype=T. When T=FnType, it creates a symbolic function.

julia> Symbolics.variable(:x, 4, 2, 0)
+(t, :runtime_symbol_value, :value_b, :value_c)
source
Symbolics.variableFunction
variable(name::Symbol, idx::Integer...; T=Real)

Create a variable with the given name along with subscripted indices with the symtype=T. When T=FnType, it creates a symbolic function.

julia> Symbolics.variable(:x, 4, 2, 0)
 x₄ˏ₂ˏ₀
 
 julia> Symbolics.variable(:x, 4, 2, 0, T=Symbolics.FnType)
-x₄ˏ₂ˏ₀⋆

Also see variables.

source
Symbolics.variablesFunction
variables(name::Symbol, indices...)

Create a multi-dimensional array of individual variables named with subscript notation. Use @variables instead to create symbolic array variables (as opposed to array of variables). See variable to create one variable with subscripts.

julia> Symbolics.variables(:x, 1:3, 3:6)
+x₄ˏ₂ˏ₀⋆

Also see variables.

source
Symbolics.variablesFunction
variables(name::Symbol, indices...)

Create a multi-dimensional array of individual variables named with subscript notation. Use @variables instead to create symbolic array variables (as opposed to array of variables). See variable to create one variable with subscripts.

julia> Symbolics.variables(:x, 1:3, 3:6)
 3×4 Matrix{Num}:
  x₁ˏ₃  x₁ˏ₄  x₁ˏ₅  x₁ˏ₆
  x₂ˏ₃  x₂ˏ₄  x₂ˏ₅  x₂ˏ₆
- x₃ˏ₃  x₃ˏ₄  x₃ˏ₅  x₃ˏ₆
source
Symbolics.EquationType
struct Equation

An equality relationship between two expressions.

Fields

  • lhs: The expression on the left-hand side of the equation.

  • rhs: The expression on the right-hand side of the equation.

source
Base.:~Method
~(lhs, rhs) -> Any
+ x₃ˏ₃  x₃ˏ₄  x₃ˏ₅  x₃ˏ₆
source
Symbolics.EquationType
struct Equation

An equality relationship between two expressions.

Fields

  • lhs: The expression on the left-hand side of the equation.

  • rhs: The expression on the right-hand side of the equation.

source
Base.:~Method
~(lhs, rhs) -> Any
 

Create an Equation out of two Num instances, or an Num and a Number.

Examples

julia> using Symbolics
 
 julia> @variables x y;
@@ -53,7 +53,7 @@
 (broadcast(~, A, B))[1:3,1:3]
 
 julia> A .~ 3x
-(broadcast(~, A, 3x))[1:3,1:3]
source

A note about functions restricted to Numbers

Sym and Term objects are NOT subtypes of Number. Symbolics provides a simple wrapper type called Num which is a subtype of Real. Num wraps either a Sym or a Term or any other object, defines the same set of operations as symbolic expressions and forwards those to the values it wraps. You can use Symbolics.value function to unwrap a Num.

By default, the @variables macros return Num-wrapped objects to allow calling functions which are restricted to Number or Real.

using Symbolics
+(broadcast(~, A, 3x))[1:3,1:3]
source

A note about functions restricted to Numbers

Sym and Term objects are NOT subtypes of Number. Symbolics provides a simple wrapper type called Num which is a subtype of Real. Num wraps either a Sym or a Term or any other object, defines the same set of operations as symbolic expressions and forwards those to the values it wraps. You can use Symbolics.value function to unwrap a Num.

By default, the @variables macros return Num-wrapped objects to allow calling functions which are restricted to Number or Real.

using Symbolics
 @variables t x y z(t);
 Symbolics.operation(Symbolics.value(x + y))
+ (generic function with 1053 methods)
Symbolics.operation(Symbolics.value(z))

\[ \begin{equation} z @@ -68,4 +68,4 @@ f(t)

\[ \begin{equation} 1 + t \left( \frac{2}{3} + \frac{4}{5} \pi \right) \end{equation} - \]

This will work for any floating-point input, as well as symbolic input.

Symbolic Control Flow

Control flow can be expressed in Symbolics.jl in the following ways:

Inspection Functions

Missing docstring.

Missing docstring for SymbolicUtils.iscall. Check Documenter's build log for details.

Missing docstring.

Missing docstring for SymbolicUtils.operation. Check Documenter's build log for details.

Missing docstring.

Missing docstring for SymbolicUtils.arguments. Check Documenter's build log for details.

+ \]

This will work for any floating-point input, as well as symbolic input.

Symbolic Control Flow

Control flow can be expressed in Symbolics.jl in the following ways:

Inspection Functions

Missing docstring.

Missing docstring for SymbolicUtils.iscall. Check Documenter's build log for details.

Missing docstring.

Missing docstring for SymbolicUtils.operation. Check Documenter's build log for details.

Missing docstring.

Missing docstring for SymbolicUtils.arguments. Check Documenter's build log for details.

diff --git a/dev/search_index.js b/dev/search_index.js index a202c767f..69b008c88 100644 --- a/dev/search_index.js +++ b/dev/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"manual/faq/#Frequently-Asked-Questions","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"","category":"section"},{"location":"manual/faq/#Limits-of-Symbolic-Computation","page":"Frequently Asked Questions","title":"Limits of Symbolic Computation","text":"","category":"section"},{"location":"manual/faq/#Transforming-my-function-to-a-symbolic-equation-has-failed.-What-do-I-do?","page":"Frequently Asked Questions","title":"Transforming my function to a symbolic equation has failed. What do I do?","text":"","category":"section"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"If you see the error:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"ERROR: TypeError: non-boolean (Num) used in boolean context","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"this is likely coming from an algorithm which cannot be traced into a purely symbolic algorithm. Many numerical solvers, for instance, have this property. It shows up when you're doing something like if x < tol. If x is a number, then this is true or false. If x is a symbol, then it's x < tol, so Julia just cannot know how many iterations to do and throws an error.","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"This shows up in adaptive algorithms, for example:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"function factorial(x)\n out = x\n while x > 1\n x -= 1\n out *= x\n end\n out\nend","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"The number of iterations this algorithm runs for is dependent on the value of x, and so there is no static representation of the algorithm. If x is 5, then it's out = x*(x-1)*(x-2)*(x-3)*(x-4), while if x is 3, then it's out = x*(x-1)*(x-2). It should thus be no surprise that:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"using Symbolics\n@variables x\ntry\n factorial(x)\ncatch e\n e\nend","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"fails. It's not that there is anything wrong with this code, but it's not going to work because fundamentally this is not a symbolically-representable algorithm.","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"The space of algorithms which can be turned into symbolic algorithms is what we call quasi-static, that is, there is a way to represent the algorithm as static. Loops are allowed, but the amount of loop iterations should not require that you know the value of the symbol x. If the algorithm is quasi-static, then Symbolics.jl tracing will produce the static form of the code, unrolling the operations, and generating a flat representation of the algorithm.","category":"page"},{"location":"manual/faq/#What-can-be-done?","page":"Frequently Asked Questions","title":"What can be done?","text":"","category":"section"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"If you need to represent this function f symbolically, then you'll need to make sure it's not traced and instead is directly represented in the underlying computational graph. Just like how sqrt(x) symbolically does not try to represent the underlying algorithm, this must be done to your f. This is done by doing @register_symbolic f(x). If you have to define things like derivatives to f, then the function registration documentation.","category":"page"},{"location":"manual/faq/#Equality-and-set-membership-tests","page":"Frequently Asked Questions","title":"Equality and set membership tests","text":"","category":"section"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"Comparing symbols with == produces a symbolic equality, not a Bool. To produce a Bool, call isequal.","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"To test if a symbol is part of a collection of symbols, i.e., a vector, either create a Set and use in, e.g.","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"try \n x in [x]\ncatch e\n e\nend","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"x in Set([x])","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"any(isequal(x), [x])","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"If == is used instead, you will receive TypeError: non-boolean (Num) used in boolean context. What this error is telling you is that the symbolic x == y expression is being used where a Bool is required, such as if x == y, and since the symbolic expression is held lazily this will error because the appropriate branch cannot be selected (since x == y is unknown for arbitrary symbolic values!). This is why the check isequal(x,y) is required, since this is a non-lazy check of whether the symbol x is always equal to the symbol y, rather than an expression of whether x and y currently have the same value.","category":"page"},{"location":"manual/faq/#Understanding-the-Difference-Between-the-Julia-Variable-and-the-Symbolic-Variable","page":"Frequently Asked Questions","title":"Understanding the Difference Between the Julia Variable and the Symbolic Variable","text":"","category":"section"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"In the most basic usage of Symbolics, the name of the Julia variable and the symbolic variable are the same. For example, when we do:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"@variables a","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"the name of the symbolic variable is a and same with the Julia variable. However, we can de-couple these by setting a to a new symbolic variable, for example:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"b = only(@variables(a))","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"Now the Julia variable b refers to the variable named a. However, the downside of this current approach is that it requires that the user writing the script knows the name a that they want to place to the variable. But what if for example we needed to get the variable's name from a file?","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"To do this, one can interpolate a symbol into the @variables macro using $. For example:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"a = :c\nb = only(@variables($a))","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"In this example, @variables($a) created a variable named c, and set this variable to b.","category":"page"},{"location":"comparison/#Comparison-of-Julia's-Symbolics.jl-vs-SymPy-for-Symbolic-Computation","page":"Comparison Against SymPy","title":"Comparison of Julia's Symbolics.jl vs SymPy for Symbolic Computation","text":"","category":"section"},{"location":"comparison/","page":"Comparison Against SymPy","title":"Comparison Against SymPy","text":"Symbolics.jl is a symbolic modeling language for Julia, built in Julia. Its goal is very different from Sympy: it was made to support symbolic-numerics, the combination of symbolic computing with numerical methods to allow for extreme performance computing that would not be possible without modifying the model. Because of this, Symbolics.jl excels in many areas due to purposeful design decisions:","category":"page"},{"location":"comparison/","page":"Comparison Against SymPy","title":"Comparison Against SymPy","text":"Performance: Symbolics.jl is built in Julia, whereas SymPy was built in Python. Thus, the performance bar for Symbolics.jl is much higher. Symbolics.jl started because SymPy was far too slow and SymEngine was far too inflexible for the projects they were doing. Performance is key to Symbolics.jl. If you find any performance issues, please file an issue.\nbuild_function: lambdify is “fine” for some people, but if you're building a super fast MPI-enabled Julia/C/Fortran simulation code, having a function that hits the Python interpreter is less than optimal. By default, build_function builds fast JIT-compiled functions due to being in Julia. However, it has support for things like static arrays, non-allocating functions via mutation, fast functions on sparse matrices and arrays of arrays, etc.: all core details of doing high performance computing.\nParallelism: Symbolics.jl has pervasive parallelism. The symbolic simplification via SymbolicUtils.jl has built-in parallelism, Symbolics.jl builds functions that parallelize across threads. Symbolics.jl is compatible with GPU libraries like CUDA.jl.\nExtensible: Symbolics.jl and its underlying tools are written in pure Julia. Want to add new or better simplification rules? Add some Julia code! Need to add new derivatives? Add some Julia code! You get the picture. Breaking down these barriers makes it easier for the user to tailor the program to their needs and accelerates the development of the library.\nDeep integration with the Julia ecosystem: Symbolics.jl's integration with neural networks is not the only thing that's deep. Symbolics.jl is built with the same philosophy as other SciML packages, eschewing “monorepos” for a distributed development approach that ties together the work of many developers. The differentiation parts utilize tools from automatic differentiation libraries, all linear algebra functionality comes from tracing Julia Base itself, symbolic rewriting (simplification and substitution) comes from SymbolicUtils.jl, parallelism comes from Julia Base libraries, Dagger.jl, etc. SciML Tools like DataDrivenDiffEq.jl can reconstruct symbolic expressions from neural networks and data, while NeuralPDE.jl can automatically solve partial differential equations from symbolic descriptions using physics-informed neural networks. The list keeps going. All told, by design Symbolics.jl's development moves fast because it's effectively using the work of hundreds of Julia developers, allowing it to grow fast.\nWhile Symbolics.jl has many features missing from SymPy, it does not superset SymPy's functionality. For a list of missing features, see this issue.","category":"page"},{"location":"manual/groebner/#Groebner-bases","page":"Groebner bases","title":"Groebner bases","text":"","category":"section"},{"location":"manual/groebner/","page":"Groebner bases","title":"Groebner bases","text":"Groebner bases use the implementation of the F4 algorithm from Groebner.jl package as its backend. We refer to the documentation of Groebner.jl, which lists some implementations details and possible use-cases of Groebner bases.","category":"page"},{"location":"manual/groebner/","page":"Groebner bases","title":"Groebner bases","text":"groebner_basis","category":"page"},{"location":"manual/groebner/#Symbolics.groebner_basis","page":"Groebner bases","title":"Symbolics.groebner_basis","text":"groebner_basis(polynomials)\n\nComputes a Groebner basis of the ideal generated by the given polynomials.\n\nThis function requires a Groebner bases backend (such as Groebner.jl) to be loaded.\n\n\n\n\n\n","category":"function"},{"location":"tutorials/perturbation/#perturb_alg","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"","category":"section"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Symbolics.jl is a fast and modern Computer Algebra System (CAS) written in Julia. It is an integral part of the SciML ecosystem of differential equation solvers and scientific machine learning packages. While Symbolics.jl is primarily designed for modern scientific computing (e.g. automatic differentiation and machine learning), it is also a powerful CAS that can be used for classic scientific computing. One such application is using perturbation theory to solve algebraic and differential equations.","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Perturbation methods are a collection of techniques to solve hard problems that generally don't have a closed solution, but depend on a tunable parameter and have closed or easy solutions for some values of this parameter. The main idea is to assume a solution that is a power series in the tunable parameter (say ϵ), such that ϵ = 0 corresponds to an easy solution, and then solve iteratively for higher-order corrections.","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"The hallmark of perturbation theory is the generation of long and convoluted intermediate equations by this process. These are subjected to algorithmic and mechanical manipulations, which makes perturbation methods an excellent fit for a CAS. In fact, CAS software have been used for perturbation calculations since the early 1970s.","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"This tutorial shows how to mix symbolic manipulations and numerical methods to solve algebraic equations with perturbation theory. Another tutorial applies it to differential equations.","category":"page"},{"location":"tutorials/perturbation/#Solving-the-quintic-equation","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Solving the quintic equation","text":"","category":"section"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"The “hello world!” analog of perturbation problems is to find a real solution x to the quintic (fifth-order) equation","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"using Symbolics\n@variables x\nquintic = x^5 + x ~ 1","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"According to Abel's theorem, a general quintic equation does not have a closed form solution. But we can easily solve it numerically using Newton's method (here implemented for simplicity, and not performance):","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"function solve_newton(eq, x, x₀; abstol=1e-8, maxiters=50)\n # symbolic expressions for f(x) and f′(x)\n f = eq.lhs - eq.rhs # want to find root of f(x)\n f′ = Symbolics.derivative(f, x)\n\n xₙ = x₀ # numerical value of the initial guess\n for i = 1:maxiters\n # calculate new guess by numerically evaluating symbolic expression at previous guess\n xₙ₊₁ = substitute(x - f / f′, x => xₙ)\n if abs(xₙ₊₁ - xₙ) < abstol\n return xₙ₊₁ # converged\n else\n xₙ = xₙ₊₁\n end\n end\n error(\"Newton's method failed to converge\")\nend\n\nx_newton = solve_newton(quintic, x, 1.0)\nprintln(\"Newton's method solution: x = \", x_newton)","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"To solve the same problem with perturbation theory, we must introduce an expansion variable ϵ in the equation:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"@variables ϵ # expansion variable\nquintic = x^5 + ϵ*x ~ 1","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"If ϵ = 1, we get our original problem. With ϵ = 0, the problem transforms to the easy quintic equation x^5 = 1 with the trivial real solution x = 1 (and four complex solutions which we ignore). Next, expand x as a power series in ϵ:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"x_coeffs, = @variables a[0:7] # create Taylor series coefficients\nx_taylor = series(x_coeffs, ϵ) # expand x in a power series in ϵ","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Then insert this into the quintic equation and expand it, too, to the same order:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"quintic_taylor = substitute(quintic, x => x_taylor)\nquintic_taylor = taylor(quintic_taylor, ϵ, 0:7)","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"This messy equation must hold for each power of ϵ, so we can separate it into one nicer equation per order:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"quintic_eqs = taylor_coeff(quintic_taylor, ϵ, 0:7)\nquintic_eqs[1:5] # for readability, show only 5 shortest equations","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"These equations show three important features of perturbation theory:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"The 0-th order equation is trivial in x_0: here x_0^5 = 1 has the trivial real solution x_0 = 1.\nThe n-th order equation is linear in x_n (except the trivial 0-th order equation).\nThe equations are triangular in x_n: the n-th order equation can be solved for x_n given only x_m for mn.","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"This structure is what makes the perturbation theory so attractive: we can start with the trivial solution x_0 = 1, then linearly solve for x_n order-by-order and substitute in the solutions of x_m for mn obtained so far.","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Here is a simple function that uses this cascading strategy to solve such a set of equations eqs for the variables xs, given a solution x₀ of the first equation eqs[begin]:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"function solve_cascade(eqs, xs, x₀, ϵ)\n sol = Dict(xs[begin] => x₀) # store solutions in a map\n\n # verify that x₀ is a solution of the first equation\n eq0 = substitute(eqs[1], sol)\n isequal(eq0.lhs, eq0.rhs) || error(\"$sol does not solve $(eqs[1])\")\n\n # solve remaining equations sequentially\n for i in 2:length(eqs)\n eq = substitute(eqs[i], sol) # insert previous solutions\n x = xs[begin+i-1] # need not be 1-indexed\n xsol = Symbolics.symbolic_linear_solve(eq, x) # solve current equation\n sol = merge(sol, Dict(x => xsol)) # store solution\n end\n\n return sol\nend","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Let us solve our order-separated quintics for the coefficients, and substitute them into the full series for x:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"x_coeffs_sol = solve_cascade(quintic_eqs, x_coeffs, 1, ϵ)\nx_pert = substitute(x_taylor, x_coeffs_sol)","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"The n-th order solution of our original quintic equation is the sum up to the ϵ^n-th order term, evaluated at ϵ=1:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"for n in 0:7\n x_pert_sol = substitute(taylor(x_pert, ϵ, 0:n), ϵ => 1)\n println(\"$n-th order solution: x = $x_pert_sol = $(x_pert_sol * 1.0)\")\nend","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"This is close to the solution from Newton's method!","category":"page"},{"location":"tutorials/perturbation/#Solving-Kepler's-Equation","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Solving Kepler's Equation","text":"","category":"section"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Historically, perturbation methods were first invented to calculate orbits of the Moon and the planets. In homage to this history, our second example is to solve Kepler's equation, which is central to solving two-body Keplerian orbits:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"@variables e E M\nkepler = E - e * sin(E) ~ M","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"We want to solve for the eccentric anomaly E given the eccentricity e and mean anomaly M. This is also easy with Newton's method. With Earth's eccentricity e = 001671 and M = pi2:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"vals_earth = Dict(e => 0.01671, M => π/2)\nE_newton = solve_newton(substitute(kepler, vals_earth), E, π/2)\nprintln(\"Newton's method solution: E = \", E_newton)","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Next, let us solve the same problem with the perturbative method. It is most common to expand E as a series in M. Repeating the procedure from the quintic example, we get these equations:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"E_taylor = series(E, M, 0:5) # this auto-creates coefficients E[0:5]\nE_coeffs = taylor_coeff(E_taylor, M) # get a handle to the coefficients\nkepler_eqs = taylor_coeff(substitute(kepler, E => E_taylor), M, 0:5)\nkepler_eqs[1:4] # for readability","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"The trivial 0-th order solution (when M=0) is E_0=0. This gives this full perturbative solution:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"E_coeffs_sol = solve_cascade(kepler_eqs, E_coeffs, 0, M)\nE_pert = substitute(E_taylor, E_coeffs_sol)","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Numerically, the result again converges to that from Newton's method:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"for n in 0:5\n println(\"$n-th order solution: E = \", substitute(taylor(E_pert, M, 0:n), vals_earth))\nend","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"But unlike Newtons method, this example shows how perturbation theory also gives us the powerful symbolic series solution for E (before numbers for e and M are inserted). Our series matches this result from Wikipedia:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"E_wiki = 1/(1-e)*M - e/(1-e)^4*M^3/factorial(3) + (9e^2+e)/(1-e)^7*M^5/factorial(5)","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Alternatively, we can expand E in e instead of M, giving the solution (the trivial solution when e = 0 is E_0=M):","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"E_taylor′ = series(E, e, 0:5)\nE_coeffs′ = taylor_coeff(E_taylor′, e)\nkepler_eqs′ = taylor_coeff(substitute(kepler, E => E_taylor′), e, 0:5)\nE_coeffs_sol′ = solve_cascade(kepler_eqs′, E_coeffs′, M, e)\nE_pert′ = substitute(E_taylor′, E_coeffs_sol′)","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"This looks very different from our first series E_pert. If they are the same, we should get 0 if we subtract and expand both as multivariate Taylor series in (eM). Indeed:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"@assert taylor(taylor(E_pert′ - E_pert, e, 0:4), M, 0:4) == 0 # use this as a test # hide\ntaylor(taylor(E_pert′ - E_pert, e, 0:4), M, 0:4)","category":"page"},{"location":"manual/types/#Supported-types-and-dispatch-in-Symbolics","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"","category":"section"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"There is a tension between types as a representation of expression trees and types that are a subtype of types already present in Julia.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"We want to be able to deal with expression trees in a unified way and not constrain expression trees themselves to be under an abstract type in Julia's type hierarchy. (For example, if we said that all expression trees are subtype of Real, then we couldn't represent array operations using the same expression tree.). But we also want to be able to pass in expression trees into places in existing code that accept Real values.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"We accomplish this by wrapping expression trees in a simple wrapper type which is a subtype of our desired abstract type. For example, we wrap expression trees in the type Num which is a subtype of Real to make it behave like a Real number.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"The methods on Num objects are forwarded to the wrapped expression tree. And care is taken so that an expression tree never internally contains Num – this is both for performance and separation of concerns.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"User-facing APIs in Symbolics always take wrapped objects like Num, they are then internally unwrapped for expression tree manipulation.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"Due to it requiring such wrappers, we only fully support a limited number of types as both the types of expression trees and the type as Julia sees them.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"These types are","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"Real numbers (wrapped using Num)\ncomplex numbers (stored as Complex{Num} where Complex is from Base Julia)\narrays of Real and complex numbers (wrapped using Arr, so Arr{Num} or Arr{Complex{Num}})","category":"page"},{"location":"manual/types/#@variables-and-types","page":"Supported types and dispatch in Symbolics","title":"@variables and types","text":"","category":"section"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"Use the syntax @variables x::T to create a symbol named x of symbolic type T. If T is a subtype of any of the above listed types which support a wrapper, the resulting variable will be wrapped in that type. As seen in the examples below, x,z,X,Z all have a suitable wrapper type. Hence, their types are shown. However, s being of symbolic type String does not have a corresponding wrapper supported by Symbolics, and hence, it returns a Sym{String} object. This is the trivial expression tree of a single variable without a wrapper, and is not a subtype of String or AbstractString.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"using Symbolics\n@variables x::Real z::Complex{Real} (X::Real)[1:10, 1:10] (Z::Complex{Real})[1:10] s::String","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"typeof(x)","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"typeof(z)","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"typeof(X)","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"typeof(Z)","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"typeof(s)","category":"page"},{"location":"manual/parsing/#Parsing-Julia-Expressions-to-Symbolic-Expressions","page":"Parsing Julia Expressions to Symbolic Expressions","title":"Parsing Julia Expressions to Symbolic Expressions","text":"","category":"section"},{"location":"manual/parsing/","page":"Parsing Julia Expressions to Symbolic Expressions","title":"Parsing Julia Expressions to Symbolic Expressions","text":"Julia expressions such as :(y - x) are fundamentally different from symbolic expressions, as they do not have an algebra defined on them. Thus, it can be very helpful when building domain-specific languages (DSLs) and parsing files to convert from Julia expressions to Symbolics.jl expressions for further manipulation. Towards this end is the parse_expr_to_symbolic which performs the parsing.","category":"page"},{"location":"manual/parsing/","page":"Parsing Julia Expressions to Symbolic Expressions","title":"Parsing Julia Expressions to Symbolic Expressions","text":"warning: Warning\nTake the limitations mentioned in the parse_expr_to_symbolic docstrings seriously! Because Julia expressions contain no symbolic metadata, there is limited information and thus the parsing requires heuristics to work.","category":"page"},{"location":"manual/parsing/","page":"Parsing Julia Expressions to Symbolic Expressions","title":"Parsing Julia Expressions to Symbolic Expressions","text":"parse_expr_to_symbolic\n@parse_expr_to_symbolic","category":"page"},{"location":"manual/parsing/#Symbolics.parse_expr_to_symbolic","page":"Parsing Julia Expressions to Symbolic Expressions","title":"Symbolics.parse_expr_to_symbolic","text":"parse_expr_to_symbolic(ex, mod::Module)\n\nApplies the parse_expr_to_symbolic function in the current module, i.e. parse_expr_to_symbolic(ex, mod) where mod is the module of the function caller.\n\nArguments\n\nex: the expression to parse\nmod: the module to apply the parsing in. See the limitations section for details.\n\nExample\n\nex = :(y(t) ~ x(t))\nparse_expr_to_symbolic(ex,Main) # gives the symbolic expression `y(t) ~ x(t)` in empty Main\n\n# Now do a whole system\n\nex = [:(y ~ x)\n :(y ~ -2x + 3 / z)\n :(z ~ 2)]\neqs = parse_expr_to_symbolic.(ex, (Main,))\n\n@variables x y z\nex = [y ~ x\n y ~ -2x + 3 / z\n z ~ 2]\nall(isequal.(eqs,ex)) # true\n\nLimitations\n\nSymbolic-ness Tied to Environment Definitions\n\nThe parsing to a symbolic expression has to be able to recognize the difference between functions, numbers, and globals defined within one's Julia environment and those that are to be made symbolic. The way this functionality handles this problem is that it does not define anything as symbolic that is already defined in the chosen mod module. For example, f(x,y) will have f as non-symbolic if the function f (named f) is defined in mod, i.e. if isdefined(mod,:f) is true. When the symbol is defined, it will be replaced by its value. Notably, this means that the parsing behavior changes depending on the environment that it is applied.\n\nFor example:\n\nparse_expr_to_symbolic(:(x - y),@__MODULE__) # x - y\nx = 2.0\nparse_expr_to_symbolic(:(x - y),@__MODULE__) # 2.0 - y\n\nThis is required to detect that standard functions like - are functions instead of symbolic symbols. For safety, one should create anonymous modules or other sub-environments to ensure no stray variables are defined.\n\nMetadata is Blank\n\nBecause all the variables defined by the expressions are not defined with the standard @variables, there is no metadata that is or can be associated with any of the generated variables. Instead, they all have blank metadata, but are defined in the Real domain. Thus, the variables which come out of this parsing may not evaluate as equal to a symbolic variable defined elsewhere.\n\n\n\n\n\n","category":"function"},{"location":"manual/variables/#Variable-and-Equation-Types","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"","category":"section"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Symbolics IR mirrors the Julia AST but allows for easy mathematical manipulation by itself following mathematical semantics. The base of the IR is the Sym type, which defines a symbolic variable. Registered (mathematical) functions on Syms (or iscall objects) return an expression that iscall. For example, op1 = x+y is one symbolic object and op2 = 2z is another, and so op1*op2 is another tree object. Then, at the top, an Equation, normally written as op1 ~ op2, defines the symbolic equality between two operations.","category":"page"},{"location":"manual/variables/#Types","page":"Variable and Equation Types","title":"Types","text":"","category":"section"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Sym, Term, and FnType are from SymbolicUtils.jl. Note that in Symbolics, we always use Sym{Real}, Term{Real}, and FnType{Tuple{Any}, Real}. To get the arguments of an iscall object, use arguments(t::Term), and to get the operation, use operation(t::Term). However, note that one should never dispatch on Term or test isa Term. Instead, one needs to use SymbolicUtils.iscall to check if arguments and operation is defined.","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"@variables\nSymbolics.variable\nSymbolics.variables\nEquation\nBase.:~(::Num, ::Num)","category":"page"},{"location":"manual/variables/#Symbolics.@variables","page":"Variable and Equation Types","title":"Symbolics.@variables","text":"Define one or more unknown variables.\n\n@variables t α σ(..) β[1:2]\n@variables w(..) x(t) y z(t, α, x)\n\nexpr = β[1]* x + y^α + σ(3) * (z - t) - β[2] * w(t - 1)\n\n(..) signifies that the value should be left uncalled.\n\nSymbolics supports creating variables that denote an array of some size.\n\njulia> @variables x[1:3]\n1-element Vector{Symbolics.Arr{Num, 1}}:\n x[1:3]\n\njulia> @variables y[1:3, 1:6] # support for tensors\n1-element Vector{Symbolics.Arr{Num, 2}}:\n y[1:3,1:6]\n\njulia> @variables t z(t)[1:3] # also works for dependent variables\n2-element Vector{Any}:\n t\n (z(t))[1:3]\n\nA symbol or expression that represents an array can be turned into an array of symbols or expressions using the scalarize function.\n\njulia> Symbolics.scalarize(z)\n3-element Vector{Num}:\n (z(t))[1]\n (z(t))[2]\n (z(t))[3]\n\nNote that @variables returns a vector of all the defined variables.\n\n@variables can also take runtime symbol values by the $ interpolation operator, and in this case, @variables doesn't automatically assign the value, instead, it only returns a vector of symbolic variables. All the rest of the syntax also applies here.\n\njulia> a, b, c = :runtime_symbol_value, :value_b, :value_c\n(:runtime_symbol_value, :value_b, :value_c)\n\njulia> vars = @variables t $a $b(t) $c(t)[1:3]\n4-element Vector{Any}:\n t\n runtime_symbol_value\n value_b(t)\n (value_c(t))[1:3]\n\njulia> (t, a, b, c)\n(t, :runtime_symbol_value, :value_b, :value_c)\n\n\n\n\n\n","category":"macro"},{"location":"manual/variables/#Symbolics.variable","page":"Variable and Equation Types","title":"Symbolics.variable","text":"variable(name::Symbol, idx::Integer...; T=Real)\n\nCreate a variable with the given name along with subscripted indices with the symtype=T. When T=FnType, it creates a symbolic function.\n\njulia> Symbolics.variable(:x, 4, 2, 0)\nx₄ˏ₂ˏ₀\n\njulia> Symbolics.variable(:x, 4, 2, 0, T=Symbolics.FnType)\nx₄ˏ₂ˏ₀⋆\n\nAlso see variables.\n\n\n\n\n\n","category":"function"},{"location":"manual/variables/#Symbolics.variables","page":"Variable and Equation Types","title":"Symbolics.variables","text":"variables(name::Symbol, indices...)\n\nCreate a multi-dimensional array of individual variables named with subscript notation. Use @variables instead to create symbolic array variables (as opposed to array of variables). See variable to create one variable with subscripts.\n\njulia> Symbolics.variables(:x, 1:3, 3:6)\n3×4 Matrix{Num}:\n x₁ˏ₃ x₁ˏ₄ x₁ˏ₅ x₁ˏ₆\n x₂ˏ₃ x₂ˏ₄ x₂ˏ₅ x₂ˏ₆\n x₃ˏ₃ x₃ˏ₄ x₃ˏ₅ x₃ˏ₆\n\n\n\n\n\n","category":"function"},{"location":"manual/variables/#Symbolics.Equation","page":"Variable and Equation Types","title":"Symbolics.Equation","text":"struct Equation\n\nAn equality relationship between two expressions.\n\nFields\n\nlhs: The expression on the left-hand side of the equation.\nrhs: The expression on the right-hand side of the equation.\n\n\n\n\n\n","category":"type"},{"location":"manual/variables/#Base.:~-Tuple{Num, Num}","page":"Variable and Equation Types","title":"Base.:~","text":"~(lhs, rhs) -> Any\n\n\nCreate an Equation out of two Num instances, or an Num and a Number.\n\nExamples\n\njulia> using Symbolics\n\njulia> @variables x y;\n\njulia> @variables A[1:3, 1:3] B[1:3, 1:3];\n\njulia> x ~ y\nx ~ y\n\njulia> x - y ~ 0\nx - y ~ 0\n\njulia> A ~ B\n(broadcast(~, A, B))[1:3,1:3]\n\njulia> A .~ 3x\n(broadcast(~, A, 3x))[1:3,1:3]\n\n\n\n\n\n","category":"method"},{"location":"manual/variables/#A-note-about-functions-restricted-to-Numbers","page":"Variable and Equation Types","title":"A note about functions restricted to Numbers","text":"","category":"section"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Sym and Term objects are NOT subtypes of Number. Symbolics provides a simple wrapper type called Num which is a subtype of Real. Num wraps either a Sym or a Term or any other object, defines the same set of operations as symbolic expressions and forwards those to the values it wraps. You can use Symbolics.value function to unwrap a Num.","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"By default, the @variables macros return Num-wrapped objects to allow calling functions which are restricted to Number or Real.","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"using Symbolics\n@variables t x y z(t);\nSymbolics.operation(Symbolics.value(x + y))","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Symbolics.operation(Symbolics.value(z))","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Symbolics.arguments(Symbolics.value(x + y))","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Note that Julia converts irrationals — like π and ℯ — to Float64 whenever they are involved in arithmetic with other numbers, including integers. An expression like 2π will be converted to a float immediately, so an expression like 2π * x will leave the symbolic x multiplied by a Float64. It may be preferable to have a symbolic representation of π also, which can be achieved with Num(π). For generic programming, it may be helpful to simply redefine the variable π to be of the same type as some other argument, as in","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"function f(x)\n let π=oftype(x, π)\n 1 + (2//3 + 4π/5) * x\n end\nend\nf(t)","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"This will work for any floating-point input, as well as symbolic input.","category":"page"},{"location":"manual/variables/#Symbolic-Control-Flow","page":"Variable and Equation Types","title":"Symbolic Control Flow","text":"","category":"section"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Control flow can be expressed in Symbolics.jl in the following ways:","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"IfElse.ifelse(cond,x,y): this is a dispatch-able version of the ifelse function provided by IfElse.jl which allows for encoding conditionals in the symbolic branches.","category":"page"},{"location":"manual/variables/#Inspection-Functions","page":"Variable and Equation Types","title":"Inspection Functions","text":"","category":"section"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"SymbolicUtils.iscall\nSymbolicUtils.operation\nSymbolicUtils.arguments","category":"page"},{"location":"manual/sparsity_detection/#Structure-and-Sparsity-Detection","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"","category":"section"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"Using the tracing system provided by Symbolics.jl expressions, Symbolics.jl can automatically detect the sparsity patterns of Julia functions efficiently. This functionality is described in more detail in the paper:","category":"page"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"@article{gowda2019sparsity,\n title={Sparsity Programming: Automated Sparsity-Aware Optimizations in Differentiable Programming},\n author={Gowda, Shashi and Ma, Yingbo and Churavy, Valentin and Edelman, Alan and Rackauckas, Christopher},\n year={2019}\n}","category":"page"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"Please cite this work if the functionality is used.","category":"page"},{"location":"manual/sparsity_detection/#Sparsity-Detection","page":"Structure and Sparsity Detection","title":"Sparsity Detection","text":"","category":"section"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"Symbolics.jacobian_sparsity\nSymbolics.hessian_sparsity","category":"page"},{"location":"manual/sparsity_detection/#Symbolics.jacobian_sparsity","page":"Structure and Sparsity Detection","title":"Symbolics.jacobian_sparsity","text":"jacobian_sparsity(\n exprs::AbstractArray,\n vars::AbstractArray\n) -> SparseArrays.SparseMatrixCSC{Bool, Int64}\n\n\nReturn the sparsity pattern of the Jacobian of an array of expressions with respect to an array of variable expressions.\n\nArguments\n\nexprs: an array of symbolic expressions.\nvars: an array of symbolic variables.\n\nExamples\n\njulia> using Symbolics\n\njulia> vars = @variables x₁ x₂;\n\njulia> exprs = [2x₁, 3x₂, 4x₁ * x₂];\n\njulia> Symbolics.jacobian_sparsity(exprs, vars)\n3×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 4 stored entries:\n 1 ⋅\n ⋅ 1\n 1 1\n\n\n\n\n\njacobian_sparsity(\n f!::Function,\n output::AbstractArray,\n input::AbstractArray,\n args...;\n kwargs...\n) -> SparseArrays.SparseMatrixCSC{Bool, Int64}\n\n\nReturn the sparsity pattern of the Jacobian of the mutating function f!.\n\nArguments\n\nf!: an in-place function f!(output, input, args...; kwargs...).\noutput: output array.\ninput: input array.\n\nThe eltype of output and input can be either symbolic or primitive.\n\nExamples\n\njulia> using Symbolics\n\njulia> f!(y, x) = y .= [x[2], 2x[1], 3x[1] * x[2]];\n\njulia> output = Vector{Float64}(undef, 3);\n\njulia> input = Vector{Float64}(undef, 2);\n\njulia> Symbolics.jacobian_sparsity(f!, output, input)\n3×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 4 stored entries:\n ⋅ 1\n 1 ⋅\n 1 1\n\n\n\n\n\n","category":"function"},{"location":"manual/sparsity_detection/#Symbolics.hessian_sparsity","page":"Structure and Sparsity Detection","title":"Symbolics.hessian_sparsity","text":"hessian_sparsity(\n expr,\n vars::AbstractVector;\n full\n) -> Union{SparseArrays.SparseMatrixCSC{Bool, Int64}, SparseArrays.SparseMatrixCSC{Int64, Int64}}\n\n\nReturn the sparsity pattern of the Hessian of an expression with respect to an array of variable expressions.\n\nArguments\n\nexpr: a symbolic expression.\nvars: a vector of symbolic variables.\n\nExamples\n\njulia> using Symbolics\n\njulia> vars = @variables x₁ x₂;\n\njulia> expr = 3x₁^2 + 4x₁ * x₂;\n\njulia> Symbolics.hessian_sparsity(expr, vars)\n2×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 3 stored entries:\n 1 1\n 1 ⋅\n\n\n\n\n\nhessian_sparsity(\n f::Function,\n input::AbstractVector,\n args...;\n full,\n kwargs...\n) -> Union{SparseArrays.SparseMatrixCSC{Bool, Int64}, SparseArrays.SparseMatrixCSC{Int64, Int64}}\n\n\nReturn the sparsity pattern of the Hessian of the given function f.\n\nArguments\n\nf: an out-of-place function f(input, args...; kwargs...).\ninput: a vector of input values whose eltype can be either symbolic or primitive.\n\nExamples\n\njulia> using Symbolics\n\njulia> f(x) = 4x[1] * x[2] - 5x[2]^2;\n\njulia> input = Vector{Float64}(undef, 2);\n\njulia> Symbolics.hessian_sparsity(f, input)\n2×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 3 stored entries:\n ⋅ 1\n 1 1\n\n\n\n\n\n","category":"function"},{"location":"manual/sparsity_detection/#Structure-Detection","page":"Structure and Sparsity Detection","title":"Structure Detection","text":"","category":"section"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"Symbolics.islinear\nSymbolics.isaffine","category":"page"},{"location":"manual/sparsity_detection/#Symbolics.islinear","page":"Structure and Sparsity Detection","title":"Symbolics.islinear","text":"islinear(ex, u)\n\n\nCheck if an expression is linear with respect to a list of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/sparsity_detection/#Symbolics.isaffine","page":"Structure and Sparsity Detection","title":"Symbolics.isaffine","text":"isaffine(ex, u)\n\n\nCheck if an expression is affine with respect to a list of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/sparsity_detection/#ADTypes.jl-interface","page":"Structure and Sparsity Detection","title":"ADTypes.jl interface","text":"","category":"section"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"Symbolics.SymbolicsSparsityDetector","category":"page"},{"location":"manual/sparsity_detection/#Symbolics.SymbolicsSparsityDetector","page":"Structure and Sparsity Detection","title":"Symbolics.SymbolicsSparsityDetector","text":"SymbolicsSparsityDetector <: ADTypes.AbstractSparsityDetector\n\nSparsity detection algorithm based on the Symbolics.jl tracing system.\n\nThis type makes Symbolics.jl compatible with the ADTypes.jl sparsity detection framework. The following functions are implemented:\n\nADTypes.jacobian_sparsity based on Symbolics.jacobian_sparsity\nADTypes.hessian_sparsity based on Symbolics.hessian_sparsity\n\nReference\n\nSparsity Programming: Automated Sparsity-Aware Optimizations in Differentiable Programming, Gowda et al. (2019)\n\n\n\n\n\n","category":"type"},{"location":"tutorials/auto_parallel/#Automated-Sparse-Parallelism-of-Julia-Functions-via-Tracing","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"","category":"section"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"Because the Symbolics.jl expressions obey Julia semantics, one can directly transform existing Julia functions into Symbolics.jl symbolic representations of the function by simply inputting the symbolic values into the function and using what is returned. For example, let's take the following numerical PDE discretization:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"using Symbolics, LinearAlgebra, SparseArrays, Plots\n\n# Define the constants for the PDE\nconst α₂ = 1.0\nconst α₃ = 1.0\nconst β₁ = 1.0\nconst β₂ = 1.0\nconst β₃ = 1.0\nconst r₁ = 1.0\nconst r₂ = 1.0\nconst _DD = 100.0\nconst γ₁ = 0.1\nconst γ₂ = 0.1\nconst γ₃ = 0.1\nconst N = 32\nconst X = reshape([i for i in 1:N for j in 1:N], N, N)\nconst Y = reshape([j for i in 1:N for j in 1:N], N, N)\nconst α₁ = 1.0 .* (X .>= 4*N/5)\n\nconst Mx = Array(Tridiagonal([1.0 for i in 1:N-1], [-2.0 for i in 1:N], [1.0 for i in 1:N-1]))\nconst My = copy(Mx)\nMx[2, 1] = 2.0\nMx[end-1,end] = 2.0\nMy[1, 2] = 2.0\nMy[end,end-1] = 2.0\n\n# Define the discretized PDE as an ODE function\nfunction f(u, p, t)\n A = u[:,:,1]\n B = u[:,:,2]\n C = u[:,:,3]\n MyA = My*A\n AMx = A*Mx\n DA = @. _DD*(MyA + AMx)\n dA = @. DA + α₁ - β₁*A - r₁*A*B + r₂*C\n dB = @. α₂ - β₂*B - r₁*A*B + r₂*C\n dC = @. α₃ - β₃*C + r₁*A*B - r₂*C\n cat(dA, dB, dC, dims=3)\nend","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"We can build the Symbolics version of this model by tracing the model function:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"# Define the initial condition as normal arrays\n@variables u[1:N, 1:N, 1:3]\ndu = simplify.(f(collect(u), nothing, 0.0))\nvec(du)[1:10]","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"The output, here the in-place modified du, is a symbolic representation of each output of the function. We can then utilize this in the Symbolics functionality. For example, let's build a parallel version of f first:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"fastf = eval(Symbolics.build_function(du,u,\n parallel=Symbolics.MultithreadedForm())[2])","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"Now let's compute the sparse Jacobian function and compile a fast multithreaded version:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"jac = Symbolics.sparsejacobian(vec(du), vec(u))\nrow,col,val = findnz(jac)\nscatter(row,col,legend=false,ms=1,c=:black)","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"fjac = eval(Symbolics.build_function(jac,u,\n parallel=Symbolics.MultithreadedForm())[2])","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"It takes awhile for this to generate, but the results will be worth it! Now let's set up the parabolic PDE to be solved by DifferentialEquations.jl. We will set up the vanilla version and the sparse multithreaded version:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"using OrdinaryDiffEq\nu0 = zeros(N, N, 3)\nMyA = zeros(N, N);\nAMx = zeros(N, N);\nDA = zeros(N, N);\nprob = ODEProblem(f, u0, (0.0, 10.0))\nfastprob = ODEProblem(ODEFunction((du, u, p, t) -> fastf(du, u),\n jac = (du, u, p, t) -> fjac(du, u),\n jac_prototype = similar(jac, Float64)),\n u0, (0.0, 10.0))","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"Let's see the timing difference:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"using BenchmarkTools\n#@btime solve(prob, TRBDF2()); # 33.073 s (895404 allocations: 23.87 GiB)\n#warning the following solve takes a long time to compile, but afterwards is very fast.\n#@btime solve(fastprob, TRBDF2()); # 209.670 ms (8208 allocations: 109.25 MiB)","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"Boom, an automatic 157x acceleration that grows as the size of the problem increases!","category":"page"},{"location":"manual/io/#I/O,-Saving,-and-Latex","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"","category":"section"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"Note that Julia's standard I/O functionality can be used to save Symbolics expressions out to files. For example, here we will generate an in-place version of f and save the anonymous function to a .jl file:","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"using Symbolics\n@variables u[1:3]\nfunction f(u)\n [u[1]-u[3],u[1]^2-u[2],u[3]+u[2]]\nend\nex1, ex2 = build_function(f(u),u)\nwrite(\"function.jl\", string(ex2))","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"Now we can do something like:","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"g = include(\"function.jl\")","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"and that will load the function back in. Note that this can be done to save the transformation results of Symbolics.jl so that they can be stored and used in a precompiled Julia package.","category":"page"},{"location":"manual/io/#Latexification","page":"I/O, Saving, and Latex","title":"Latexification","text":"","category":"section"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"Symbolics.jl's expressions support Latexify.jl, and thus","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"using Latexify\nlatexify(ex)","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"will produce LaTeX output from Symbolics models and expressions. This works on basics like Term all the way to higher primitives like ODESystem and ReactionSystem.","category":"page"},{"location":"manual/solver/#Solver","page":"Solver","title":"Solver","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"The main symbolic solver for Symbolics.jl is symbolic_solve. Symbolic solving means that it only uses symbolic (algebraic) methods and outputs exact solutions.","category":"page"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"Symbolics.symbolic_solve","category":"page"},{"location":"manual/solver/#Symbolics.symbolic_solve","page":"Solver","title":"Symbolics.symbolic_solve","text":"symbolic_solve(expr, x; dropmultiplicity=true, warns=true)\n\nsymbolic_solve is a function which attempts to solve input equations/expressions symbolically using various methods.\n\nArguments\n\nexpr: Could be a single univar expression in the form of a poly or multiple univar expressions or multiple multivar polys or a transcendental nonlinear function.\nx: Could be a single variable or an array of variables which should be solved\ndropmultiplicity (optional): Should the output be printed n times where n is the number of occurrence of the root? Say we have (x+1)^2, we then have 2 roots x = -1, by default the output is [-1], If dropmultiplicity is inputted as false, then the output is [-1, -1].\nwarns (optional): When invalid expressions or cases are inputted, should the solver warn you of such cases before returning nothing? if this is set to false, the solver returns nothing. By default, warns are set to true.\n\nSupported input\n\nThe base solver (symbolic_solve) has multiple solvers which chooses from depending on the the type of input (multiple/uni var and multiple/single expression) only after ensuring that the input is valid.\n\nThe expressions inputted can contain parameters, which are assumed to be transcendental. A parameter \"a\" is transcendental if there exists no polynomial P with rational coefficients such that P(a) = 0. Check the examples section.\n\nCurrently, symbolic_solve supports\n\nLinear and polynomial equations (with parameters)\nSystems of linear and polynomials equations (without extra parameters, for now)\nEquations with transcendental functions (with parameters)\n\nExamples\n\nsolve_univar (uses factoring and analytic solutions up to degree 4)\n\nnote: Note\nThe package Nemo is needed in order to use solve_univar as well as solve_multipoly, so executing using Nemo as you will see in the following examples is necessary; otherwise, the function will throw an error.\n\njulia> using Symbolics, Nemo;\n\njulia> @variables x a b;\n\njulia> expr = expand((x + b)*(x^2 + 2x + 1)*(x^2 - a))\n-a*b - a*x - 2a*b*x - 2a*(x^2) + b*(x^2) + x^3 - a*b*(x^2) - a*(x^3) + 2b*(x^3) + 2(x^4) + b*(x^4) + x^5\n\njulia> symbolic_solve(expr, x)\n4-element Vector{Any}:\n -1\n -b\n (1//2)*√(4a)\n (-1//2)*√(4a)\n\njulia> symbolic_solve(expr, x, dropmultiplicity=false)\n5-element Vector{Any}:\n -1\n -1\n -b\n (1//2)*√(4a)\n (-1//2)*√(4a)\n\njulia> symbolic_solve(x^2 + a*x + 6, x)\n2-element Vector{SymbolicUtils.BasicSymbolic{Real}}:\n (1//2)*(-a + √(-24 + a^2))\n (1//2)*(-a - √(-24 + a^2))\n\njulia> symbolic_solve(x^7 - 1, x)\n2-element Vector{Any}:\n roots_of((1//1) + x + x^2 + x^3 + x^4 + x^5 + x^6, x)\n 1\n\nsolve_multivar (uses Groebner basis and solve_univar to find roots)\n\nnote: Note\nSimilar to solve_univar, Groebner is needed for solve_multivar or to be fully functional.\n\njulia> using Groebner\n\njulia> @variables x y z\n3-element Vector{Num}:\n x\n y\n z\n\njulia> eqs = [x+y^2+z, z*x*y, z+3x+y]\n3-element Vector{Num}:\n x + z + y^2\n x*y*z\n 3x + y + z\n\njulia> symbolic_solve(eqs, [x,y,z])\n3-element Vector{Any}:\n Dict{Num, Any}(z => 0, y => 1//3, x => -1//9)\n Dict{Num, Any}(z => 0, y => 0, x => 0)\n Dict{Num, Any}(z => -1, y => 1, x => 0)\n\nnote: Note\nIf Nemo or Groebner are not imported when needed, the solver throws an error.\n\njulia> using Symbolics\n\njulia> @variables x y z;\n\njulia> symbolic_solve(x+1, x)\nERROR: \"Nemo is required. Execute `using Nemo` to enable this functionality.\"\n\njulia> symbolic_solve([x+1, y], [x, y])\nERROR: \"Groebner bases engine is required. Execute `using Groebner` to enable this functionality.\"\n\nsolve_multipoly (uses GCD between the input polys)\n\njulia> symbolic_solve([x-1, x^3 - 1, x^2 - 1, (x-1)^20], x)\n1-element Vector{BigInt}:\n 1\n\nia_solve (solving by isolation and attraction)\n\njulia> symbolic_solve(2^(x+1) + 5^(x+3), x)\n1-element Vector{SymbolicUtils.BasicSymbolic{Real}}:\n (-slog(2) - log(complex(-1)) + 3slog(5)) / (slog(2) - slog(5))\n\njulia> symbolic_solve(log(x+1)+log(x-1), x)\n2-element Vector{SymbolicUtils.BasicSymbolic{BigFloat}}:\n (1//2)*√(8.0)\n (-1//2)*√(8.0)\n\njulia> symbolic_solve(a*x^b + c, x)\n((-c)^(1 / b)) / (a^(1 / b))\n\nEvaluating output (converting to floats)\n\nIf you want to evaluate the exact expressions found by symbolic_solve, you can do the following:\n\njulia> roots = symbolic_solve(2^(x+1) + 5^(x+3), x)\n1-element Vector{SymbolicUtils.BasicSymbolic{Real}}:\n (-slog(2) - log(complex(-1)) + 3slog(5)) / (slog(2) - slog(5))\n\njulia> Symbolics.symbolic_to_float.(roots)\n1-element Vector{Complex{BigFloat}}:\n -4.512941594732059759689023145584186058252768936052415430071569066192919491762214 + 3.428598090438030380369414618548038962770087500755160535832807433942464545729382im\n\n\n\n\n\n","category":"function"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"One other symbolic solver is symbolic_linear_solve which is limited compared to symbolic_solve as it only solves linear equations.","category":"page"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"Symbolics.symbolic_linear_solve","category":"page"},{"location":"manual/solver/#Symbolics.symbolic_linear_solve","page":"Solver","title":"Symbolics.symbolic_linear_solve","text":"symbolic_linear_solve(eq, var; simplify, check) -> Any\n\n\nSolve equation(s) eqs for a set of variables vars.\n\nAssumes length(eqs) == length(vars)\n\nCurrently only works if all equations are linear. check if the expr is linear w.r.t vars.\n\nExamples\n\njulia> @variables x y\n2-element Vector{Num}:\n x\n y\n\njulia> Symbolics.symbolic_linear_solve(x + y ~ 0, x)\n-y\n\njulia> Symbolics.symbolic_linear_solve([x + y ~ 0, x - y ~ 2], [x, y])\n2-element Vector{Float64}:\n 1.0\n -1.0\n\n\n\n\n\n","category":"function"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"symbolic_solve only supports symbolic, i.e. non-floating point computations, and thus prefers equations where the coefficients are integer, rational, or symbolic. Floating point coefficients are transformed into rational values and BigInt values are used internally with a potential performance loss, and thus it is recommended that this functionality is only used with floating point values if necessary. In contrast, symbolic_linear_solve directly handles floating point values using standard factorizations.","category":"page"},{"location":"manual/solver/#More-technical-details-and-examples","page":"Solver","title":"More technical details and examples","text":"","category":"section"},{"location":"manual/solver/#Technical-details","page":"Solver","title":"Technical details","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"The symbolic_solve function uses 4 hidden solvers in order to solve the user's input. Its base, solve_univar, uses analytic solutions up to polynomials of degree 4 and factoring as its method for solving univariate polynomials. The function's solve_multipoly uses GCD on the input polynomials then throws passes the result to solve_univar. The function's solve_multivar uses Groebner basis and a separating form in order to create linear equations in the input variables and a single high degree equation in the separating variable [1]. Each equation resulting from the basis is then passed to solve_univar. We can see that essentially, solve_univar is the building block of symbolic_solve. If the input is not a valid polynomial and can not be solved by the algorithm above, symbolic_solve passes it to ia_solve, which attempts solving by attraction and isolation [2]. This only works when the input is a single expression and the user wants the answer in terms of a single variable. Say log(x) - a == 0 gives us [e^a].","category":"page"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"Symbolics.solve_univar\nSymbolics.solve_multivar\nSymbolics.ia_solve\nSymbolics.ia_conditions!\nSymbolics.is_periodic\nSymbolics.fundamental_period","category":"page"},{"location":"manual/solver/#Symbolics.solve_univar","page":"Solver","title":"Symbolics.solve_univar","text":"solve_univar(expression, x; dropmultiplicity=true)\n\nThis solver uses analytic solutions up to degree 4 to solve univariate polynomials. It first handles the special case of the expression being of operation ^. E.g. math (x+2)^{20}. We solve this by removing the int 20, then solving the poly math x+2 on its own. If the parameter mult of the solver is set to true, we then repeat the found roots of math x+2 twenty times before returning the results to the user.\n\nStep 2 is filtering the expression after handling this special case, and then factoring it using factor_use_nemo. We then solve all the factors outputted using the analytic methods implemented in the function get_roots and its children.\n\nArguments\n\nexpr: Single symbolics Num or SymbolicUtils.BasicSymbolic expression. This is equated to 0 and then solved. E.g. expr = x+2, we solve x+2 = 0\nx: Single symbolics variable\ndropmultiplicity (optional): Print repeated roots or not?\nstrict (optional): Bool that enables/disables strict assert if input expression is a univariate polynomial or not. If strict=true and expression is not a polynomial, solve_univar throws an assertion error.\n\nExamples\n\n\n\n\n\n","category":"function"},{"location":"manual/solver/#Symbolics.ia_solve","page":"Solver","title":"Symbolics.ia_solve","text":"ia_solve(lhs, var; kwargs...)\n\nThis function attempts to solve transcendental functions by first checking the \"smart\" number of occurrences in the input LHS. By smart here we mean that polynomials are counted as 1 occurrence. for example x^2 + 2x is 1 occurrence of x. So we abstract all occurrences of x's as polynomials. Say: log(x+1) + x^2 is seen as log(f(x)) + g(x) so there are 2 occurrences of x. If there is only 1 occurrence of x in an input expression, isolate is called.\n\nIsolate reverses all operations applied on the occurrence of x until we have f(x) = some constant then we can solve this using our polynomial solvers.\n\nIf more than 1 occurrence of x is found, ia_solve attempts to attract the occurrences of x in order to reduce these occurrences to 1. For example, log(x+1) + log(x-1) can be converted to log(x^2 - 1) which now could be isolated using Isolate.\n\nattract(lhs, var) currently uses 4 techniques for attraction.\n\nLog addition: log(f(x)) + log(g(x)) => log(h(x))\nExponential simplification: a*b^(f(x)) + c*d^(g(x)) => f(x) * log(b) - g(x) * log(d) + log(-a/c). And now this is actually 1 occurrence of x since f(x) and g(x) are just multiplied by constants not wrapped in some operation.\nTrig simplification: this bruteforces multiple trig identities and doesn't detect them before hand.\nPolynomialization: as a last resort, attract attempts to polynomialize the expression. Say sin(x+2)^2 + sin(x+2) + 10 is converted to X^2 + X + 10, we then solve this using our polynomial solver, and afterwards, isolate sin(x+2) = the roots found by solve for X^2 + X + 10\n\nAfter attraction, we check the number of occurrences again, and if its 1, we isolate, if not, we throw an error to tell the user that this is currently unsolvable by our covered techniques.\n\nArguments\n\nlhs: a Num/SymbolicUtils.BasicSymbolic\nvar: variable to solve for.\n\nKeyword arguments\n\nwarns = true: Whether to emit warnings for unsolvable expressions.\ncomplex_roots = true: Whether to consider complex roots of x ^ n ~ y, where n is an integer.\nperiodic_roots = true: If true, isolate f(x) ~ y as x ~ finv(y) + n * period where is_periodic(f) == true, finv = left_inverse(f) and period = fundamental_period(f). n is a new anonymous symbolic variable.\n\nExamples\n\njulia> solve(a*x^b + c, x)\n((-c)^(1 / b)) / (a^(1 / b))\n\njulia> solve(2^(x+1) + 5^(x+3), x)\n1-element Vector{SymbolicUtils.BasicSymbolic{Real}}:\n (-log(2) + 3log(5) - log(complex(-1))) / (log(2) - log(5))\n\njulia> solve(log(x+1)+log(x-1), x)\n2-element Vector{SymbolicUtils.BasicSymbolic{Real}}:\n (1//2)*RootFinding.ssqrt(8.0)\n (-1//2)*RootFinding.ssqrt(8.0)\n\njulia> expr = sin(x+2)^2 + sin(x+2) + 10\n10 + sin(2 + x) + sin(2 + x)^2\n\njulia> RootFinding.ia_solve(expr, x)\n[ Info: var\"##230\" ϵ Ζ: e.g. 0, 1, 2...\n[ Info: var\"##234\" ϵ Ζ: e.g. 0, 1, 2...\n2-element Vector{SymbolicUtils.BasicSymbolic{Real}}:\n -2 + π*2var\"##230\" + asin((1//2)*(-1 + RootFinding.ssqrt(-39)))\n -2 + π*2var\"##234\" + asin((1//2)*(-1 - RootFinding.ssqrt(-39)))\n\nAll transcendental functions for which left_inverse is defined are supported. To enable ia_solve to handle custom transcendental functions, define an inverse or left inverse. If the function is periodic, is_periodic and fundamental_period must be defined. If the function imposes certain conditions on its input or output (for example, log requires that its input be positive) define ia_conditions!.\n\nSee also: left_inverse, inverse, is_periodic, fundamental_period, ia_conditions!.\n\nReferences\n\n[1]: R. W. Hamming, Coding and Information Theory, ScienceDirect, 1980.\n\n\n\n\n\n","category":"function"},{"location":"manual/solver/#Symbolics.ia_conditions!","page":"Solver","title":"Symbolics.ia_conditions!","text":"ia_conditions!(f, lhs, rhs::Vector{Any}, conditions::Vector{Tuple})\n\nIf f is a left-invertible function, lhs and rhs[i] are univariate functions and f(lhs) ~ rhs[i] for all i in eachindex(rhss), push to conditions all the relevant conditions on lhs or rhs[i]. Each condition is of the form (sym, op) where sym is an expression involving lhs and/or rhs[i] and op is a binary relational operator. The condition op(sym, 0) is then required to be true for the equation f(lhs) ~ rhs[i] to be valid.\n\nFor example, if f = log, lhs = x and rhss = [y, z] then the condition x > 0 must be true. Thus, (lhs, >) is pushed to conditions. Similarly, if f = sqrt, rhs[i] >= 0 must be true for all i, and so (y, >=) and (z, >=) will be appended to conditions. \n\n\n\n\n\n","category":"function"},{"location":"manual/solver/#Symbolics.is_periodic","page":"Solver","title":"Symbolics.is_periodic","text":"is_periodic(f)\n\nReturn true if f is a single-input single-output periodic function. Return false by default. If is_periodic(f) == true, then fundamental_period(f) must also be defined.\n\nSee also: fundamental_period\n\n\n\n\n\n","category":"function"},{"location":"manual/solver/#Symbolics.fundamental_period","page":"Solver","title":"Symbolics.fundamental_period","text":"fundamental_period(f)\n\nReturn the fundamental period of periodic function f. Must only be called if is_periodic(f) == true.\n\nsee also: is_periodic\n\n\n\n\n\n","category":"function"},{"location":"manual/solver/#Nice-examples","page":"Solver","title":"Nice examples","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"using Symbolics, Nemo;\n@variables x;\nSymbolics.symbolic_solve(9^x + 3^x ~ 8, x)","category":"page"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"@variables x y z;\nSymbolics.symbolic_linear_solve(2//1*x + y - 2//1*z ~ 9//1*x, 1//1*x)","category":"page"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"using Groebner;\n@variables x y z;\n\neqs = [x^2 + y + z - 1, x + y^2 + z - 1, x + y + z^2 - 1]\nSymbolics.symbolic_solve(eqs, [x,y,z])","category":"page"},{"location":"manual/solver/#Feature-completeness","page":"Solver","title":"Feature completeness","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"[x] Linear and polynomial equations\n[x] Systems of linear and polynomial equations\n[x] Some transcendental functions\n[x] Systems of linear equations with parameters (via symbolic_linear_solve)\n[ ] Equations with radicals\n[x] Systems of polynomial equations with parameters and positive dimensional systems\n[ ] Inequalities","category":"page"},{"location":"manual/solver/#Expressions-we-can-not-solve-(but-aim-to)","page":"Solver","title":"Expressions we can not solve (but aim to)","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"# Mathematica\n\nIn[1]:= Reduce[x^2 - x - 6 > 0, x]\nOut[1]= x < -2 || x > 3\n\nIn[2]:= Reduce[x+a > 0, x]\nOut[2]= a \\[Element] Reals && x > -a\n\nIn[3]:= Solve[x^(x) + 3 == 0, x]\nOut[3]= {{x -> (I \\[Pi] + Log[3])/ProductLog[I \\[Pi] + Log[3]]}}","category":"page"},{"location":"manual/solver/#References","page":"Solver","title":"References","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"[1]: Rouillier, F. Solving Zero-Dimensional Systems Through the Rational Univariate Representation. AAECC 9, 433–461 (1999).","category":"page"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"[2]: R. W. Hamming, Coding and Information Theory, ScienceDirect, 1980.","category":"page"},{"location":"tutorials/converting_to_C/#Automatic-Conversion-of-Julia-Code-to-C-Functions","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"","category":"section"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"Since Symbolics.jl can trace Julia code into Symbolics IR that can be built and compiled via build_function to C, this gives us a nifty way to automatically generate C functions from Julia code! To see this in action, let's start with the Lotka-Volterra equations:","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"using Symbolics\nfunction lotka_volterra!(du, u, p, t)\n x, y = u\n α, β, δ, γ = p\n du[1] = dx = α*x - β*x*y\n du[2] = dy = -δ*y + γ*x*y\nend","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"Now we trace this into Symbolics:","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"@variables t du[1:2] u[1:2] p[1:4]\ndu = collect(du)\nlotka_volterra!(du, u, p, t)\ndu","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"and then we build the function:","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"build_function(du, u, p, t, target=Symbolics.CTarget())","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"If we want to compile this, we do expression=Val{false}:","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"f = build_function(du, u, p, t, target=Symbolics.CTarget(), expression=Val{false})","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"now we check it computes the same thing:","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"du = rand(2); du2 = rand(2)\nu = rand(2)\np = rand(4)\nt = rand()\nf(du, u, p, t)\nlotka_volterra!(du2, u, p, t)\ndu == du2 # true!","category":"page"},{"location":"manual/build_function/#Function-Building-and-Compilation-(build_function)","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"","category":"section"},{"location":"manual/build_function/","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"At any time, callable functions can be generated from Symbolics IR by using Symbolics.toexpr. This performs some cleaning to return an expression without extraneous pieces that commonly matches expressions one would write in functions like those for differential equation solvers and optimization libraries. These functions can be automatically parallelized and specialize on Julia types like static arrays and sparse matrices.","category":"page"},{"location":"manual/build_function/","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"The core compilation process of Symbolics IR is build_function. build_function takes an operation or an AbstractArray of operations and generates a compilable version of the model for numerical solvers. The form of this output is dependent on the target. By default, the target outputs Julia code, but other formats, such as C, Stan, and MATLAB are available. These can be generated as expressions which can then be evaluated into a callable function, or the compilers for the respective targets can be invoked to directly give back the function handle.","category":"page"},{"location":"manual/build_function/#build_function","page":"Function Building and Compilation (build_function)","title":"build_function","text":"","category":"section"},{"location":"manual/build_function/","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"build_function","category":"page"},{"location":"manual/build_function/#Symbolics.build_function","page":"Function Building and Compilation (build_function)","title":"Symbolics.build_function","text":"build_function(ex, args...;\n expression = Val{true},\n target = JuliaTarget(),\n parallel=nothing,\n kwargs...)\n\nGenerates a numerically-usable function from a Symbolics Num.\n\nArguments:\n\nex: The Num to compile\nargs: The arguments of the function\nexpression: Whether to generate code or whether to generate the compiled form. By default, expression = Val{true}, which means that the code for the function is returned. If Val{false}, then the returned value is compiled.\n\nKeyword Arguments:\n\ntarget: The output target of the compilation process. Possible options are:\nJuliaTarget: Generates a Julia function\nCTarget: Generates a C function\nStanTarget: Generates a function for compiling with the Stan probabilistic programming language\nMATLABTarget: Generates an anonymous function for use in MATLAB and Octave environments\nparallel: The kind of parallelism to use in the generated function. Defaults to SerialForm(), i.e. no parallelism, if ex is a single expression or an array containing <= 1500 non-zero expressions. If ex is an array of > 1500 non-zero expressions, then ShardedForm(80, 4) is used. See below for more on ShardedForm. Note that the parallel forms are not exported and thus need to be chosen like Symbolics.SerialForm(). The choices are:\nSerialForm(): Serial execution.\nShardedForm(cutoff, ncalls): splits the output function into sub-functions which contain at most cutoff number of output rhss. These sub-functions are called by the top-level function that buildfunction returns. This helps in reducing the compile time of the generated function.\nMultithreadedForm(): Multithreaded execution with a static split, evenly splitting the number of expressions per thread.\nfname: Used by some targets for the name of the function in the target space.\n\nNote that not all build targets support the full compilation interface. Check the individual target documentation for details.\n\n\n\n\n\n","category":"function"},{"location":"manual/build_function/#Target-Specific-Definitions","page":"Function Building and Compilation (build_function)","title":"Target-Specific Definitions","text":"","category":"section"},{"location":"manual/build_function/","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"Symbolics._build_function(target::Symbolics.JuliaTarget,rhss::AbstractArray,args...;kwargs...)\nSymbolics._build_function(target::Symbolics.CTarget,eqs::Array{<:Equation},args...;kwargs...)\nSymbolics._build_function(target::Symbolics.StanTarget,eqs::Array{<:Equation}, vs, ps, iv;kwargs...)\nSymbolics._build_function(target::Symbolics.MATLABTarget,eqs::Array{<:Equation},args...;kwargs...)","category":"page"},{"location":"manual/build_function/#Symbolics._build_function-Tuple{Symbolics.JuliaTarget, AbstractArray, Vararg{Any}}","page":"Function Building and Compilation (build_function)","title":"Symbolics._build_function","text":"_build_function(target::JuliaTarget, rhss::AbstractArray, args...;\n conv=toexpr,\n expression = Val{true},\n expression_module = @__MODULE__(),\n checkbounds = false,\n postprocess_fbody=ex -> ex,\n linenumbers = false,\n outputidxs=nothing,\n skipzeros = false,\n force_SA = false,\n wrap_code = (nothing, nothing),\n fillzeros = skipzeros && !(rhss isa SparseMatrixCSC),\n states = LazyState(),\n iip_config = (true, true),\n parallel=nothing, cse = false, kwargs...)\n\nBuild function target: JuliaTarget\n\n_build_function(target::JuliaTarget, rhss, args...;\n conv = toexpr,\n expression = Val{true},\n checkbounds = false,\n linenumbers = false,\n headerfun = addheader, outputidxs=nothing,\n convert_oop = true, force_SA = false,\n skipzeros = outputidxs===nothing,\n fillzeros = skipzeros && !(typeof(rhss)<:SparseMatrixCSC),\n parallel=SerialForm(), kwargs...)\n\nGenerates a Julia function which can then be utilized for further evaluations. If expression=Val{false}, the return is a Julia function which utilizes RuntimeGeneratedFunctions.jl to be free of world-age issues.\n\nIf the rhss is a scalar, the generated function is a function with a scalar output. Otherwise, if it's an AbstractArray, the output is two functions, one for out-of-place AbstractArray output and a second which is a mutating function. The outputted functions match the given argument order, i.e., f(u,p,args...) for the out-of-place and scalar functions and f!(du,u,p,args..) for the in-place version.\n\nSpecial Keyword Arguments:\n\nparallel: The kind of parallelism to use in the generated function. Defaults to SerialForm(), i.e. no parallelism. Note that the parallel forms are not exported and thus need to be chosen like Symbolics.SerialForm(). The choices are:\nSerialForm(): Serial execution.\nShardedForm(cutoff, ncalls): splits the output function into sub-functions which contain at most cutoff number of output rhss. These sub-functions are called by the top-level function that buildfunction returns.\nMultithreadedForm(): Multithreaded execution with a static split, evenly splitting the number of expressions per thread.\nconv: The conversion function of symbolic types to Expr. By default, this uses the toexpr function.\ncheckbounds: For whether to enable bounds checking inside the generated function. Defaults to false, meaning that @inbounds is applied.\nlinenumbers: Determines whether the generated function expression retains the line numbers. Defaults to true.\nconvert_oop: Determines whether the OOP version should try to convert the output to match the type of the first input. This is useful for cases like LabelledArrays or other array types that carry extra information. Defaults to true.\nforce_SA: Forces the output of the OOP version to be a StaticArray. Defaults to false, and outputs a static array when the first argument is a static array.\nskipzeros: Whether to skip filling zeros in the in-place version if the filling function is 0.\nfillzeros: Whether to perform fill(out,0) before the calculations to ensure safety with skipzeros.\n\n\n\n\n\n","category":"method"},{"location":"manual/build_function/#Symbolics._build_function-Tuple{Symbolics.CTarget, Array{<:Equation}, Vararg{Any}}","page":"Function Building and Compilation (build_function)","title":"Symbolics._build_function","text":"Build function target: CTarget\n\n_build_function(target::CTarget, eqs::Array{<:Equation}, args...;\n conv = toexpr, expression = Val{true},\n fname = :diffeqf,\n lhsname=:du,rhsnames=[Symbol(\"RHS$i\") for i in 1:length(args)],\n libpath=tempname(), compiler=:gcc)\n\nThis builds an in-place C function. Only works on arrays of equations. If expression == Val{false}, then this builds a function in C, compiles it, and returns a lambda to that compiled function. These special keyword arguments control the compilation:\n\nlibpath: the path to store the binary. Defaults to a temporary path.\ncompiler: which C compiler to use. Defaults to :gcc, which is currently the only available option.\n\n\n\n\n\n","category":"method"},{"location":"manual/build_function/#Symbolics._build_function-Tuple{Symbolics.StanTarget, Array{<:Equation}, Any, Any, Any}","page":"Function Building and Compilation (build_function)","title":"Symbolics._build_function","text":"Build function target: StanTarget\n\n_build_function(target::StanTarget, eqs::Array{<:Equation}, vs, ps, iv;\n conv = toexpr, expression = Val{true},\n fname = :diffeqf, lhsname=:internal_var___du,\n rhsnames=[:internal_var___u,:internal_var___p,:internal_var___t])\n\nThis builds an in-place Stan function compatible with the Stan differential equation solvers. Unlike other build targets, this one requires (vs, ps, iv) as the function arguments. Only allowed on arrays of equations.\n\n\n\n\n\n","category":"method"},{"location":"manual/build_function/#Symbolics._build_function-Tuple{Symbolics.MATLABTarget, Array{<:Equation}, Vararg{Any}}","page":"Function Building and Compilation (build_function)","title":"Symbolics._build_function","text":"Build function target: MATLABTarget\n\n_build_function(target::MATLABTarget, eqs::Array{<:Equation}, args...;\n conv = toexpr, expression = Val{true},\n lhsname=:internal_var___du,\n rhsnames=[:internal_var___u,:internal_var___p,:internal_var___t])\n\nThis builds an out of place anonymous function @(t,rhsnames[1]) to be used in MATLAB. Compatible with the MATLAB differential equation solvers. Only allowed on expressions, and arrays of expressions.\n\n\n\n\n\n","category":"method"},{"location":"manual/build_function/#Limitations","page":"Function Building and Compilation (build_function)","title":"Limitations","text":"","category":"section"},{"location":"manual/build_function/","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"build_function ","category":"page"},{"location":"manual/arrays/#symbolic_arrays","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"","category":"section"},{"location":"manual/arrays/#Symbolic-Arrays-vs-Arrays-of-Symbolic-Expressions","page":"Symbolic Arrays","title":"Symbolic Arrays vs Arrays of Symbolic Expressions","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Symbolics.jl contains two forms for handling symbolic arrays:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Arrays of symbolic expressions: these are Julia arrays with Symbolics.jl objects in them.\nSymbolic Arrays: these are symbolic (O(1)) representations of arrays.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Arrays of symbolic expressions are simply Symbolics.jl objects put into Julia arrays. For example:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"using Symbolics\n@variables x y\nu = [x,y]","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"is a vector of two symbolic variables. As shorthand,","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"u2 = Symbolics.variables(:x, 1:3, 3:6)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"creates a Julia matrix of symbolic variables. Indexing u or u2 gives symbolic values which act as a normal scalar symbolic value. This form these uses Julia's array functionality and performs symbolic operations on the scalar values.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"On the otherhand, Julia's symbolic array form is an O(1) representation of the whole array.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"@variables A[1:5, 1:3]","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"When using this form, A[1,1] is not a symbolic variable but a symbolic expression for indexing the variable A. This representation holds linear algebra expressions in a non-expanded form. For example:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"@variables B[1:3, 1:3]\nA * B","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"in comparison to:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"a = Symbolics.variables(:a, 1:5, 1:3)\nb = Symbolics.variables(:b, 1:3, 1:3)\na * b","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"This makes the symbolic array form much more efficient, but requires that the expressions uses things with registered symbolic array functions which currently has much lower coverage. Also, there are many fallbacks for which arrays of symbolics which makes this approach more accessible but with larger expressions.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"We recommend defaulting to arrays of symbolics unless you need the expression symplifications of the symbolic array approach.","category":"page"},{"location":"manual/arrays/#Using-Symbolic-Arrays","page":"Symbolic Arrays","title":"Using Symbolic Arrays","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Symbolic array-valued expressions (symbolic arrays) are supported by Symbolics. Symbolic array expressions propagate useful metadata that depends on input arrays: array dimension, element type and shape.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"You can create a symbolic array variable with the following syntax:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"using Symbolics\n@variables A[1:5, 1:3] b[1:3]","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Here, A is a symbolic matrix of size (5, 3) and b is a symbolic vector of length 3.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"size(A)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"size(b)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"ndims(A)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"ndims(b)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"eltype(A)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"eltype(b)","category":"page"},{"location":"manual/arrays/#Array-operations","page":"Symbolic Arrays","title":"Array operations","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Operations on symbolic arrays return symbolic array expressions:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"c = A * b","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"size(c)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"eltype(c)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Adjoints, matrix-matrix, and matrix-vector multiplications are supported. Dot product returns a scalar-valued expression:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"b'b","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"size(b'b)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Outer product returns a matrix:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"b * b'","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"size(b*b')","category":"page"},{"location":"manual/arrays/#Broadcast,-map-and-reduce","page":"Symbolic Arrays","title":"Broadcast, map and reduce","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"A .* b'","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"map(asin, (A*b))","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"#sum(A) #latexify not working","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"typeof(sum(A))","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"typeof(sum(A, dims=2))","category":"page"},{"location":"manual/arrays/#Indexing-and-delayed-computation","page":"Symbolic Arrays","title":"Indexing and delayed computation","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Indexing array expressions is fairly flexible in Symbolics. Let's go through all the possible ways to index arrays.","category":"page"},{"location":"manual/arrays/#Scalar-indexing-and-scalarization","page":"Symbolic Arrays","title":"Scalar indexing and scalarization","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"AAt = A*A'","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"AAt[2,3]","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Here we indexed for the element (2,3), but we got back a symbolic indexing expression. You may want to force the element to be computed in terms of the elements of A. This can be done, using the scalarize function.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Symbolics.scalarize(AAt[2,3])","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"@syms i::Int j::Int\nSymbolics.scalarize(AAt[i,j])","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"In general, any scalar expression which is derived from array expressions can be scalarized.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"#sum(A[:,1]) + sum(A[2,:])#latexify not working","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Symbolics.scalarize(sum(A[:,1]) + sum(A[2,:]))","category":"page"},{"location":"manual/limits/#Symbolic-Limits","page":"Symbolic Limits","title":"Symbolic Limits","text":"","category":"section"},{"location":"manual/limits/","page":"Symbolic Limits","title":"Symbolic Limits","text":"Experimental symbolic limit support is provided by the limit function, documented below. See SymbolicLimits.jl for more information and implementation details.","category":"page"},{"location":"manual/limits/","page":"Symbolic Limits","title":"Symbolic Limits","text":"limit","category":"page"},{"location":"manual/limits/#Symbolics.limit","page":"Symbolic Limits","title":"Symbolics.limit","text":"limit(expr, var, h[, side::Symbol])\n\nCompute the limit of expr as var approaches h.\n\nside indicates the direction from which var approaches h. It may be one of :left, :right, or :both. If side is :both and the two sides do not align, an error is thrown. Side defaults to :both for finite h, :left for h = Inf, and :right for h = -Inf.\n\nexpr must be composed of log, exp, constants, and the rational operators +, -, *, and /. This limitation may eventually be relaxed.\n\nwarning: Warning\nBecause symbolic limit computation is undecidable, this function necessarily employs heuristics and may occasionally return wrong answers. Nevertheless, please report wrong answers as issues as we aim to have heuristics that produce correct answers in all practical cases.\n\n\n\n\n\n","category":"function"},{"location":"manual/functions/#function_registration","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Function registration is the ability to define new nodes in the symbolic graph. This is useful because symbolic computing is declarative, i.e. symbolic computations express what should be computed, not how it should be computed. However, at some level someone must describe how a given operation is computed. These are the primitive functions, and a symbolic expression is made up of primitive functions.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Symbolics.jl comes pre-registered with a large set of standard mathematical functions, like * and sin to special functions like erf, and even deeper operations like DataInterpolations.jl's AbstractInterpolation. However, in many cases you may need to add your own function, i.e. you may want to give an imperative code and use this to define a new symbolic code. Symbolics.jl calls the declaration of new declarative primitives from an imperative function definition registration. This page describes both the registration process and its companion process, tracing, for interacting with code written in Julia.","category":"page"},{"location":"manual/functions/#Direct-Tracing","page":"Function Registration and Tracing","title":"Direct Tracing","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Because Symbolics expressions respect Julia semantics, one way to generate symbolic expressions is to simply place Symbolics variables as inputs into existing Julia code. For example, the following uses the standard Julia function for the Lorenz equations to generate the symbolic expression for the Lorenz equations:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"using Symbolics\nfunction lorenz(du,u,p,t)\n du[1] = 10.0(u[2]-u[1])\n du[2] = u[1]*(28.0-u[3]) - u[2]\n du[3] = u[1]*u[2] - (8/3)*u[3]\nend\n@variables t p[1:3] u(t)[1:3]\ndu = Array{Any}(undef, 3)\nlorenz(du,u,p,t)\ndu","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Or similarly:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"@variables t x(t) y(t) z(t) dx(t) dy(t) dz(t) σ ρ β\ndu = [dx,dy,dz]\nu = [x,y,z]\np = [σ,ρ,β]\nlorenz(du,u,p,t)\ndu","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Note that what has been done here is that the imperative Julia code for the function lorenz has been transformed into a declarative symbolic graph. Importantly, the code of lorenz is transformed into an expression consisting only of primitive registered functions, things like * and -, which come pre-registered with Symbolics.jl This then allows for symbolic manipulation of the expressions, allowing things like simplification and operation reordering done on its generated expressions. ","category":"page"},{"location":"manual/functions/#Utility-and-Scope-of-Tracing","page":"Function Registration and Tracing","title":"Utility and Scope of Tracing","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"This notably describes one limitation of tracing: tracing only works if the expression being traced is composed of already registered functions. If unregistered functions, such as calls to C code, are used, then the tracing process will error.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"However, we note that symbolic tracing by definition does not guarantee that the exact choices. The symbolic expressions may re-distribute the arithmetic, simplify out expressions, or do other modifications. Thus if this function is function is sensitive to numerical details in its calculation, one would not want to trace the function and thus would instead register it as a new primitive function.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"For the symbolic system to be as powerful in its manipulations as possible, it is recommended that the registration of functions be minimized to the simplest possible set, and thus registration should only be used when necessary. This is because any code within a registered function is treated as a blackbox imperative code that cannot be manipulated, thus decreasing the potential for simplifications.","category":"page"},{"location":"manual/functions/#Registering-Functions","page":"Function Registration and Tracing","title":"Registering Functions","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"The Symbolics graph only allows registered Julia functions within its type. All other functions are automatically traced down to registered functions. By default, Symbolics.jl pre-registers the common functions utilized in SymbolicUtils.jl and pre-defines their derivatives. However, the user can utilize the @register_symbolic macro to add their function to allowed functions of the computation graph.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Additionally, @register_array_symbolic can be used to define array functions. For size propagation it's required that a computation of how the sizes are computed is also supplied.","category":"page"},{"location":"manual/functions/#Defining-Derivatives-of-Registered-Functions","page":"Function Registration and Tracing","title":"Defining Derivatives of Registered Functions","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"In order for symbolic differentiation to work, an overload of Symbolics.derivative is required. The syntax is derivative(typeof(f), args::NTuple{i,Any}, ::Val{j}) where i is the number of arguments to the function and j is which argument is being differentiated. So for example:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"function derivative(::typeof(min), args::NTuple{2,Any}, ::Val{1})\n x, y = args\n IfElse.ifelse(x < y, one(x), zero(x))\nend","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"is the partial derivative of the Julia min(x,y) function with respect to x.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"note: Note\nDownstream symbolic derivative functionality only work if every partial derivative that is required in the derivative expression is defined. Note that you only need to define the partial derivatives which are actually computed.","category":"page"},{"location":"manual/functions/#Registration-of-Array-Functions","page":"Function Registration and Tracing","title":"Registration of Array Functions","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Similar to scalar functions, array functions can be registered to define new primitives for functions which either take in or return arrays. This is done by using the @register_array_symbolic macro. It acts similarly to the scalar function registration but requires a calculation of the input and output sizes. For example, let's assume we wanted to have a function that computes the solution to Ax = b, i.e. a linear solve, using an SVD factorization. In Julia, the code for this would be svdsolve(A,b) = svd(A)\\b. We would create this function as follows:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"using LinearAlgebra, Symbolics\n\nsvdsolve(A, b) = svd(A)\\b\n@register_array_symbolic svdsolve(A::AbstractMatrix, b::AbstractVector) begin\n size = size(b)\n eltype = promote_type(eltype(A), eltype(b))\nend","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Now using the function svdsolve with symbolic array variables will be kept lazy:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"@variables A[1:3, 1:3] b[1:3]\nsvdsolve(A,b)","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Note that at this time array derivatives cannot be defined.","category":"page"},{"location":"manual/functions/#Registration-API","page":"Function Registration and Tracing","title":"Registration API","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"@register_symbolic\n@register_array_symbolic","category":"page"},{"location":"manual/functions/#Symbolics.@register_symbolic","page":"Function Registration and Tracing","title":"Symbolics.@register_symbolic","text":"@register_symbolic(expr, define_promotion = true, Ts = [Real])\n\nOverload appropriate methods so that Symbolics can stop tracing into the registered function. If define_promotion is true, then a promotion method in the form of\n\nSymbolicUtils.promote_symtype(::typeof(f_registered), args...) = Real # or the annotated return type\n\nis defined for the register function. Note that when defining multiple register overloads for one function, all the rest of the registers must set define_promotion to false except for the first one, to avoid method overwriting.\n\nExamples\n\n@register_symbolic foo(x, y)\n@register_symbolic foo(x, y::Bool) false # do not overload a duplicate promotion rule\n@register_symbolic goo(x, y::Int) # `y` is not overloaded to take symbolic objects\n@register_symbolic hoo(x, y)::Int # `hoo` returns `Int`\n\nSee @register_array_symbolic to register functions which return arrays.\n\n\n\n\n\n","category":"macro"},{"location":"manual/functions/#Symbolics.@register_array_symbolic","page":"Function Registration and Tracing","title":"Symbolics.@register_array_symbolic","text":"@register_array_symbolic(expr, define_promotion = true)\n\nExample:\n\n# Let's say vandermonde takes an n-vector and returns an n x n matrix\n@register_array_symbolic vandermonde(x::AbstractVector) begin\n size=(length(x), length(x))\n eltype=eltype(x) # optional, will default to the promoted eltypes of x\nend\n\nYou can also register calls on callable structs:\n\n@register_array_symbolic (c::Conv)(x::AbstractMatrix) begin\n size=size(x) .- size(c.kernel) .+ 1\n eltype=promote_type(eltype(x), eltype(c))\nend\n\nIf define_promotion = true then a promotion method in the form of\n\nSymbolicUtils.promote_symtype(::typeof(f_registered), args...) = # inferred or annotated return type\n\nis defined for the register function. Note that when defining multiple register overloads for one function, all the rest of the registers must set define_promotion to false except for the first one, to avoid method overwriting.\n\n\n\n\n\n","category":"macro"},{"location":"manual/functions/#Direct-Registration-API-(Advanced,-Experimental)","page":"Function Registration and Tracing","title":"Direct Registration API (Advanced, Experimental)","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"warning: Warning\nThis is a lower level API which is not as stable as the macro APIs.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"In some circumstances you may need to use the direct API in order to define registration on functions or types without using the macro. This is done by directly defining dispatches on symbolic objects.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"A good example of this is DataInterpolations.jl's interpolations object. On an interpolation by a symbolic variable, we generate the symbolic function (the term) for the interpolation function. This looks like:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"using DataInterpolations, Symbolics, SymbolicUtils\n(interp::AbstractInterpolation)(t::Num) = SymbolicUtils.term(interp, unwrap(t))","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"In order for this to work, it is required that we define the symtype for the symbolic type inference. This is done via:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"SymbolicUtils.promote_symtype(t::AbstractInterpolation, args...) = Real","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Additionally a symbolic name is required:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Base.nameof(interp::AbstractInterpolation) = :Interpolation","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"The derivative is defined similarly to the macro case:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"function Symbolics.derivative(interp::AbstractInterpolation, args::NTuple{1, Any}, ::Val{1})\n Symbolics.unwrap(derivative(interp, Symbolics.wrap(args[1])))\nend","category":"page"},{"location":"manual/functions/#Inverse-function-registration","page":"Function Registration and Tracing","title":"Inverse function registration","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Symbolics.jl allows defining and querying the inverses of functions.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"inverse\nleft_inverse\nright_inverse\n@register_inverse\nhas_inverse\nhas_left_inverse\nhas_right_inverse","category":"page"},{"location":"manual/functions/#Symbolics.inverse","page":"Function Registration and Tracing","title":"Symbolics.inverse","text":"inverse(f)\n\nGiven a single-input single-output function f, return its inverse g. This requires that f is bijective. If inverse is defined for a function, left_inverse and right_inverse should return inverse(f). inverse(g) should also be defined to return f.\n\nSee also: left_inverse, right_inverse, @register_inverse.\n\n\n\n\n\n","category":"function"},{"location":"manual/functions/#Symbolics.left_inverse","page":"Function Registration and Tracing","title":"Symbolics.left_inverse","text":"left_inverse(f)\n\nGiven a single-input single-output function f, return its left inverse g. This requires that f is injective. If left_inverse is defined for a function, right_inverse and inverse must not be defined and should error. right_inverse(g) should also be defined to return f.\n\nSee also: inverse, right_inverse, @register_inverse.\n\n\n\n\n\n","category":"function"},{"location":"manual/functions/#Symbolics.right_inverse","page":"Function Registration and Tracing","title":"Symbolics.right_inverse","text":"right_inverse(f)\n\nGiven a single-input single-output function f, return its right inverse g. This requires that f is surjective. If right_inverse is defined for a function, left_inverse and inverse must not be defined and should error. left_inverse(g) should also be defined to return f.\n\nSee also inverse, left_inverse, @register_inverse.\n\n\n\n\n\n","category":"function"},{"location":"manual/functions/#Symbolics.@register_inverse","page":"Function Registration and Tracing","title":"Symbolics.@register_inverse","text":"@register_inverse f g\n@register_inverse f g left\n@register_inverse f g right\n\nMark f and g as inverses of each other. By default, assume that f and g are bijective. Also defines left_inverse and right_inverse to call inverse. If the third argument is left, assume that f is injective and g is its left inverse. If the third argument is right, assume that f is surjective and g is its right inverse.\n\n\n\n\n\n","category":"macro"},{"location":"manual/functions/#Symbolics.has_inverse","page":"Function Registration and Tracing","title":"Symbolics.has_inverse","text":"has_inverse(_) -> Bool\n\n\nCheck if the provided function has an inverse defined via inverse. Uses hasmethod to perform the check.\n\n\n\n\n\n","category":"function"},{"location":"manual/functions/#Symbolics.has_left_inverse","page":"Function Registration and Tracing","title":"Symbolics.has_left_inverse","text":"has_left_inverse(_) -> Bool\n\n\nCheck if the provided function has a left inverse defined via left_inverse Uses hasmethod to perform the check.\n\n\n\n\n\n","category":"function"},{"location":"manual/functions/#Symbolics.has_right_inverse","page":"Function Registration and Tracing","title":"Symbolics.has_right_inverse","text":"has_right_inverse(_) -> Bool\n\n\nCheck if the provided function has a left inverse defined via left_inverse Uses hasmethod to perform the check.\n\n\n\n\n\n","category":"function"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Symbolics.jl implements inverses for standard trigonometric and logarithmic functions, as well as their variants from NaNMath. It also implements inverses of ComposedFunctions.","category":"page"},{"location":"getting_started/#Getting-Started-with-Symbolics.jl","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.jl is a symbolic modeling language. In this tutorial, we will walk you through the process of getting Symbolics.jl up and running, and start doing our first symbolic calculations.","category":"page"},{"location":"getting_started/#Installing-Symbolics.jl","page":"Getting Started with Symbolics.jl","title":"Installing Symbolics.jl","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Installing Symbolics.jl is as simple as using the Julia package manager. This is done via the command ]add Symbolics. After precompilation is complete, do using Symbolics in the terminal (REPL) and when that command is completed, you're ready to start!","category":"page"},{"location":"getting_started/#Building-Symbolic-Expressions","page":"Getting Started with Symbolics.jl","title":"Building Symbolic Expressions","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"The way to define symbolic variables is via the @variables macro:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using Symbolics\n@variables x y","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"After defining variables as symbolic, symbolic expressions, which we call a iscall object, can be generated by utilizing Julia expressions. For example:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"z = x^2 + y","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Here, z is an expression tree for “square x and add y”. To make an array of symbolic expressions, simply make an array of symbolic expressions:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"A = [x^2 + y 0 2x\n 0 0 2y\n y^2 + x 0 0]","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Note that by default, @variables returns Sym or iscall objects wrapped in Num to make them behave like subtypes of Real. Any operation on these Num objects will return a new Num object, wrapping the result of computing symbolically on the underlying values.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"If you are following this tutorial in the Julia REPL, A will not be shown with LaTeX equations. To get these equations, we can use Latexify.jl. Symbolics.jl comes with Latexify recipes, so it works automatically:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using Latexify\nlatexify(A)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Normal Julia functions work on Symbolics expressions, so if we want to create the sparse version of A we would just call sparse:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using SparseArrays\nspA = sparse(A)\nlatexify(A)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"We can thus use normal Julia functions as generators for sparse expressions. For example, here we will define","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"function f(u)\n [u[1] - u[3], u[1]^2 - u[2], u[3] + u[2]]\nend\nf([x, y, z]) # Recall that z = x^2 + y","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Or we can build an array of variables and use it to trace the function:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"u = Symbolics.variables(:u, 1:5)\nf(u)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"note: Note\nSymbolics.variables(:u, 1:5) creates a Julia array of symbolic variables. This uses O(n) compute and memory but is a very general representation. Symbolics.jl also has the ability to represent symbolic arrays which gives an O(1) representation but is more limited in its functionality. For more information, see the Symbolic Arrays page.","category":"page"},{"location":"getting_started/#Derivatives","page":"Getting Started with Symbolics.jl","title":"Derivatives","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"One common thing to compute in a symbolic system is derivatives. In Symbolics.jl, derivatives are represented lazily via operations, just like any other function. To build a differential operator, use Differential like:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"@variables t\nD = Differential(t)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"This is the differential operator D = fracpartialpartial t. We can compose the differential operator by *, e.g. Differential(t) * Differential(x) or Differential(t)^2. Now let's write down the derivative of some expression:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"z = t + t^2\nD(z)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Notice that this hasn't computed anything yet: D is a lazy operator because it lets us symbolically represent “The derivative of z with respect to t”, which is useful for example when representing our favorite thing in the world, differential equations. However, if we want to expand the derivative operators, we'd use expand_derivatives:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"expand_derivatives(D(z))","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"The variable, that you are taking the derivative with respect to, is accessed with:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"D.x","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"We can also have simplified functions for multivariable calculus. For example, we can compute the Jacobian of an array of expressions like:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.jacobian([x + x*y, x^2 + y], [x, y])","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"and similarly we can do Hessians, gradients, and define whatever other derivatives you want.","category":"page"},{"location":"getting_started/#Simplification-and-Substitution","page":"Getting Started with Symbolics.jl","title":"Simplification and Substitution","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics interfaces with SymbolicUtils.jl to allow for simplifying symbolic expressions. This is done simply through the simplify command:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"simplify(2x + 2y)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"This can be applied to arrays by using Julia's broadcast mechanism:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"B = simplify.([t + t^2 + t + t^2 2t + 4t\n x + y + y + 2t x^2 - x^2 + y^2])","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"We can then use substitute to change values of an expression around:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"simplify.(substitute.(B, (Dict(x => y^2),)))","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"and we can use this to interactively evaluate expressions without generating and compiling Julia functions:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"V = substitute.(B, (Dict(x => 2.0, y => 3.0, t => 4.0),))","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Where we can reference the values via:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.value.(V)","category":"page"},{"location":"getting_started/#Non-Interactive-Development","page":"Getting Started with Symbolics.jl","title":"Non-Interactive Development","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Note that the macros are for the high-level case where you're doing symbolic computation on your own code. If you want to do symbolic computation on someone else's code, like in a macro, you may not want to do @variables x because you might want the name “x” to come from the user's code. For these cases, you can use the interpolation operator to interpolate the runtime value of x, i.e. @variables $x. Check the documentation of @variables for more details.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"a, b, c = :runtime_symbol_value, :value_b, :value_c","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"vars = @variables t $a $b(t) $c(t)[1:3]","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"(t, a, b, c)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"One could also use variable and variables. Read their documentation for more details.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"If we need to use this to generate new Julia code, we can simply convert the output to an Expr:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.toexpr(x + y^2)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"The other way around is also possible, parsing Julia expressions into symbolic expressions","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"ex = [:(v ~ w)\n :(w ~ -v)]\neqs = parse_expr_to_symbolic.(ex, (Main,))\neqs_lhs = [eq.lhs for eq in eqs]\neqs_rhs = [eq.rhs for eq in eqs]\nSymbolics.jacobian(eqs_rhs, eqs_lhs)","category":"page"},{"location":"getting_started/#Syms-and-callable-Syms","page":"Getting Started with Symbolics.jl","title":"Syms and callable Syms","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"In the definition","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"@variables t x(t) y(t)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"t is of type Sym{Real}, but the name x refers to an object that represents the Term x(t). The operation of this expression is itself the object Sym{FnType{Tuple{Real}, Real}}(:x). The type Sym{FnType{...}} represents a callable object. In this case specifically, it's a function that takes 1 Real argument (noted by Tuple{Real}) and returns a Real result. You can call such a callable Sym with either a number or a symbolic expression of a permissible type.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"This expression also defines t as an independent variable, while x(t) and y(t) are dependent variables. This is accounted for in differentiation:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"z = x + y*t\nexpand_derivatives(D(z))","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Since x and y are time-dependent, they are not automatically eliminated from the expression, and thus the D(x) and D(y) operations still exist in the expanded derivatives for correctness.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"We can also define unrestricted functions:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"@variables g(..)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Here, g is a variable that is a function of other variables. Any time that we reference g we have to utilize it as a function:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"z = g(x) + g(y)","category":"page"},{"location":"getting_started/#Registering-Functions","page":"Getting Started with Symbolics.jl","title":"Registering Functions","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"One of the benefits of a one-language Julia symbolic stack is that the primitives are all written in Julia, and therefore it's trivially extendable from Julia itself. By default, new functions are traced to the primitives and the symbolic expressions are written on the primitives. However, we can expand the allowed primitives by registering new functions. For example, let's register a new function h:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"h(x, y) = x^2 + y\n@register_symbolic h(x, y)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"When we use h(x, y), it is a symbolic expression and doesn't expand:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"h(x, y) + y^2","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"To use it with the differentiation system, we need to register its derivatives. We would do it like this:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"# Derivative w.r.t. the first argument\nSymbolics.derivative(::typeof(h), args::NTuple{2,Any}, ::Val{1}) = 2args[1]\n# Derivative w.r.t. the second argument\nSymbolics.derivative(::typeof(h), args::NTuple{2,Any}, ::Val{2}) = 1","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"and now it works with the rest of the system:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.derivative(h(x, y) + y^2, x)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.derivative(h(x, y) + y^2, y)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"note: Note\n@register_symbolic only allows for scalar outputs. If full array functions are needed, then see @register_array_symbolic for registering functions of symbolic arrays.","category":"page"},{"location":"getting_started/#Building-Functions","page":"Getting Started with Symbolics.jl","title":"Building Functions","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"The function for building functions from symbolic expressions is the aptly-named build_function. The first argument is the symbolic expression or the array of symbolic expressions to compile, and the trailing arguments are the arguments for the function. For example:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"to_compute = [x^2 + y, y^2 + x]\nf_expr = build_function(to_compute, [x, y])\nBase.remove_linenums!.(f_expr)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"gives back two codes. The first is a function f([x, y]) that computes and builds an output vector [x^2 + y, y^2 + x]. Because this tool was made to be used by all the cool kids writing fast Julia codes, it is specialized to Julia and supports features like StaticArrays. For example:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using StaticArrays\nmyf = eval(f_expr[1])\nmyf(SA[2.0, 3.0])","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"The second function is an in-place non-allocating mutating function which mutates its first value. Thus, we'd use it like the following:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"myf! = eval(f_expr[2])\nout = zeros(2)\nmyf!(out, [2.0, 3.0])\nout","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"To save the symbolic calculations for later, we can take this expression and save it out to a file:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"write(\"function.jl\", string(f_expr[2]))","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Note that if we need to avoid eval, for example to avoid world-age issues, one could do expression = Val{false}:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Base.remove_linenums!(build_function(to_compute, [x, y], expression=Val{false})[1])","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"which will use RuntimeGeneratedFunctions.jl to build Julia functions which avoid world-age issues.","category":"page"},{"location":"getting_started/#Building-Non-Allocating-Parallel-Functions-for-Sparse-Matrices","page":"Getting Started with Symbolics.jl","title":"Building Non-Allocating Parallel Functions for Sparse Matrices","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Now let's show off a little bit. build_function is kind of like if lambdify ate its spinach. To show this, let's build a non-allocating function that fills sparse matrices in a multithreaded manner. To do this, we just have to represent the operation that we're turning into a function via a sparse matrix. For example:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using LinearAlgebra\nN = 8\nA = sparse(Tridiagonal([x^i for i in 1:N-1], [x^i * y^(8-i) for i in 1:N], [y^i for i in 1:N-1]))\nshow(A)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Now we call build_function:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Base.remove_linenums!(build_function(A,[x,y],parallel=Symbolics.MultithreadedForm())[2])","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Let's unpack what that's doing. It's using A.nzval to linearly index through the sparse matrix, avoiding the A[i,j] form because that is a more expensive way to index a sparse matrix if you know exactly the order that the data is stored. Then, it's splitting up the calculation into Julia threads, so they can be operated on in parallel. It synchronizes after spawning all the tasks, so the computation is ensured to be complete before moving on. And it does this with all in-place operations to the original matrix instead of generating arrays. This is the fanciest way you could fill a sparse matrix, and Symbolics makes this dead simple.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"(Note: this example may be slower with multithreading due to the thread spawning overhead, but the full version was not included in the documentation for brevity. It will be the faster version if N is sufficiently large!)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Importantly, when using the in-place functions generated by build_function, it is important that the mutating argument matches the sparse structure of the original function, as demonstrated below.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using Symbolics, SparseArrays, LinearAlgebra\n\nN = 10\n_S = sprand(N, N, 0.1)\n_Q = sprand(N, N, 0.1)\n\nF(z) = [ # some complicated sparse amenable function\n _S * z\n _Q * z.^2\n]\n\nSymbolics.@variables z[1:N]\n\nsj = Symbolics.sparsejacobian(F(z), z) # sparse jacobian\n\nf_expr = build_function(sj, z)\n\nrows, cols, _ = findnz(sj)\nout = sparse(rows, cols, zeros(length(cols)), size(sj)...) # pre-allocate, and correct structure\nmyf = eval(last(f_expr))\nmyf(out, rand(N)) # note that out matches the sparsity structure of sj\nout","category":"page"},{"location":"manual/expression_manipulation/#Expression-Manipulation","page":"Expression Manipulation","title":"Expression Manipulation","text":"","category":"section"},{"location":"manual/expression_manipulation/","page":"Expression Manipulation","title":"Expression Manipulation","text":"Symbolics.jl provides functionality for easily manipulating expressions. Most of the functionality comes by the expression objects obeying the standard mathematical semantics. For example, if one has A a matrix of symbolic expressions wrapped in Num, then A^2 calculates the expressions for the squared matrix. It is thus encouraged to use standard Julia for performing many of the manipulation on the IR. For example, calculating the sparse form of the matrix via sparse(A) is valid, legible, and easily understandable to all Julia programmers.","category":"page"},{"location":"manual/expression_manipulation/#Functionality-Inherited-From-SymbolicUtils.jl","page":"Expression Manipulation","title":"Functionality Inherited From SymbolicUtils.jl","text":"","category":"section"},{"location":"manual/expression_manipulation/","page":"Expression Manipulation","title":"Expression Manipulation","text":"SymbolicUtils.substitute\nSymbolicUtils.simplify","category":"page"},{"location":"manual/expression_manipulation/#SymbolicUtils.substitute","page":"Expression Manipulation","title":"SymbolicUtils.substitute","text":"substitute(expr, s; fold=true)\n\nPerforms the substitution on expr according to rule(s) s. If fold=false, expressions which can be evaluated won't be evaluated.\n\nExamples\n\njulia> @variables t x y z(t)\n4-element Vector{Num}:\n t\n x\n y\n z(t)\njulia> ex = x + y + sin(z)\n(x + y) + sin(z(t))\njulia> substitute(ex, Dict([x => z, sin(z) => z^2]))\n(z(t) + y) + (z(t) ^ 2)\njulia> substitute(sqrt(2x), Dict([x => 1]); fold=false)\nsqrt(2)\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#SymbolicUtils.simplify","page":"Expression Manipulation","title":"SymbolicUtils.simplify","text":"simplify(x; expand=false,\n threaded=false,\n thread_subtree_cutoff=100,\n rewriter=nothing)\n\nSimplify an expression (x) by applying rewriter until there are no changes. expand=true applies expand in the beginning of each fixpoint iteration.\n\nBy default, simplify will assume denominators are not zero and allow cancellation in fractions. Pass simplify_fractions=false to prevent this.\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/","page":"Expression Manipulation","title":"Expression Manipulation","text":"Documentation for rewriter can be found here, using the @rule macro or the @acrule macro from SymbolicUtils.jl.","category":"page"},{"location":"manual/expression_manipulation/#Additional-Manipulation-Functions","page":"Expression Manipulation","title":"Additional Manipulation Functions","text":"","category":"section"},{"location":"manual/expression_manipulation/","page":"Expression Manipulation","title":"Expression Manipulation","text":"Other additional manipulation functions are given below.","category":"page"},{"location":"manual/expression_manipulation/","page":"Expression Manipulation","title":"Expression Manipulation","text":"Symbolics.get_variables\nSymbolics.tosymbol\nSymbolics.diff2term\nSymbolics.degree\nSymbolics.coeff\nSymbolics.replace\nSymbolics.occursin\nSymbolics.filterchildren\nSymbolics.fixpoint_sub\nSymbolics.fast_substitute\nSymbolics.symbolic_to_float","category":"page"},{"location":"manual/expression_manipulation/#Symbolics.get_variables","page":"Expression Manipulation","title":"Symbolics.get_variables","text":"get_variables(e, varlist = nothing; sort::Bool = false)\n\nReturn a vector of variables appearing in e, optionally restricting to variables in varlist.\n\nNote that the returned variables are not wrapped in the Num type.\n\nExamples\n\njulia> @variables t x y z(t);\n\njulia> Symbolics.get_variables(x + y + sin(z); sort = true)\n3-element Vector{SymbolicUtils.BasicSymbolic}:\n x\n y\n z(t)\n\njulia> Symbolics.get_variables(x - y; sort = true)\n2-element Vector{SymbolicUtils.BasicSymbolic}:\n x\n y\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.tosymbol","page":"Expression Manipulation","title":"Symbolics.tosymbol","text":"tosymbol(x::Union{Num,Symbolic}; states=nothing, escape=true) -> Symbol\n\nConvert x to a symbol. states are the states of a system, and escape means if the target has escapes like val\"y(t)\". If escape is false, then it will only output y instead of y(t).\n\nExamples\n\njulia> @variables t z(t)\n2-element Vector{Num}:\n t\n z(t)\n\njulia> Symbolics.tosymbol(z)\nSymbol(\"z(t)\")\n\njulia> Symbolics.tosymbol(z; escape=false)\n:z\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.diff2term","page":"Expression Manipulation","title":"Symbolics.diff2term","text":"diff2term(x, x_metadata::Dict{Datatype, Any}) -> Symbolic\n\nConvert a differential variable to a Term. Note that it only takes a Term not a Num. Any upstream metadata can be passed via x_metadata\n\njulia> @variables x t u(x, t) z(t)[1:2]; Dt = Differential(t); Dx = Differential(x);\n\njulia> Symbolics.diff2term(Symbolics.value(Dx(Dt(u))))\nuˍtx(x, t)\n\njulia> Symbolics.diff2term(Symbolics.value(Dt(z[1])))\nvar\"z(t)[1]ˍt\"\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.degree","page":"Expression Manipulation","title":"Symbolics.degree","text":"degree(p, sym=nothing)\n\nExtract the degree of p with respect to sym.\n\nExamples\n\njulia> @variables x;\n\njulia> Symbolics.degree(x^0)\n0\n\njulia> Symbolics.degree(x)\n1\n\njulia> Symbolics.degree(x^2)\n2\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.coeff","page":"Expression Manipulation","title":"Symbolics.coeff","text":"coeff(p, sym=nothing)\n\nExtract the coefficient of p with respect to sym. Note that p might need to be expanded and/or simplified with expand and/or simplify.\n\nExamples\n\njulia> @variables a x y;\n\njulia> Symbolics.coeff(2a, x)\n0\n\njulia> Symbolics.coeff(3x + 2y, y)\n2\n\njulia> Symbolics.coeff(x^2 + y, x^2)\n1\n\njulia> Symbolics.coeff(2*x*y + y, x*y)\n2\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Base.occursin","page":"Expression Manipulation","title":"Base.occursin","text":"occursin(needle::Symbolic, haystack::Symbolic)\n\nDetermine whether the second argument contains the first argument. Note that this function doesn't handle associativity, commutativity, or distributivity.\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.filterchildren","page":"Expression Manipulation","title":"Symbolics.filterchildren","text":"filterchildren(c, x)\n\nReturns all parts of x that fulfills the condition given in c. c can be a function or an expression. If it is a function, returns everything for which the function is true. If c is an expression, returns all expressions that matches it.\n\nExamples:\n\n@syms x\nSymbolics.filterchildren(x, log(x) + x + 1)\n\nreturns [x, x]\n\n@variables t X(t)\nD = Differential(t)\nSymbolics.filterchildren(Symbolics.is_derivative, X + D(X) + D(X^2))\n\nreturns [Differential(t)(X(t)^2), Differential(t)(X(t))]\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.fixpoint_sub","page":"Expression Manipulation","title":"Symbolics.fixpoint_sub","text":"fixpoint_sub(expr, dict; operator = Nothing, maxiters = 10000)\n\nGiven a symbolic expression, equation or inequality expr perform the substitutions in dict recursively until the expression does not change. Substitutions that depend on one another will thus be recursively expanded. For example, fixpoint_sub(x, Dict(x => y, y => 3)) will return 3. The operator keyword can be specified to prevent substitution of expressions inside operators of the given type. The maxiters keyword is used to limit the number of times the substitution can occur to avoid infinite loops in cases where the substitutions in dict are circular (e.g. [x => y, y => x]).\n\nSee also: fast_substitute.\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.fast_substitute","page":"Expression Manipulation","title":"Symbolics.fast_substitute","text":"fast_substitute(expr, dict; operator = Nothing)\n\nGiven a symbolic expression, equation or inequality expr perform the substitutions in dict. This only performs the substitutions once. For example, fast_substitute(x, Dict(x => y, y => 3)) will return y. The operator keyword can be specified to prevent substitution of expressions inside operators of the given type.\n\nSee also: fixpoint_sub.\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.symbolic_to_float","page":"Expression Manipulation","title":"Symbolics.symbolic_to_float","text":"symbolic_to_float(x::Union{Num, BasicSymbolic})::Union{AbstractFloat, BasicSymbolic}\n\nIf the symbolic value is exactly equal to a number, converts the symbolic value to a floating point number. Otherwise retains the symbolic value.\n\nExamples\n\nsymbolic_to_float((1//2 * x)/x) # 0.5\nsymbolic_to_float((1/2 * x)/x) # 0.5\nsymbolic_to_float((1//2)*√(279//4)) # 4.175823272122517\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Derivatives-and-Differentials","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"","category":"section"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"A Differential(op) is a partial derivative with respect to op, which can then be applied to some other operations. For example, D=Differential(t) is what would commonly be referred to as d/dt, which can then be applied to other operations using its function call, so D(x+y) is d(x+y)/dt.","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"By default, the derivatives are left unexpanded to capture the symbolic representation of the differential equation. If the user would like to expand out all the differentials, the expand_derivatives function eliminates all the differentials down to basic one-variable expressions.","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"Differential\nexpand_derivatives\nis_derivative","category":"page"},{"location":"manual/derivatives/#Symbolics.Differential","page":"Derivatives and Differentials","title":"Symbolics.Differential","text":"struct Differential <: Symbolics.Operator\n\nRepresents a differential operator.\n\nFields\n\nx: The variable or expression to differentiate with respect to.\n\nExamples\n\njulia> using Symbolics\n\njulia> @variables x y;\n\njulia> D = Differential(x)\n(D'~x)\n\njulia> D(y) # Differentiate y wrt. x\n(D'~x)(y)\n\njulia> Dx = Differential(x) * Differential(y) # d^2/dxy operator\n(D'~x(t)) ∘ (D'~y(t))\n\njulia> D3 = Differential(x)^3 # 3rd order differential operator\n(D'~x(t)) ∘ (D'~x(t)) ∘ (D'~x(t))\n\n\n\n\n\n","category":"type"},{"location":"manual/derivatives/#Symbolics.expand_derivatives","page":"Derivatives and Differentials","title":"Symbolics.expand_derivatives","text":"expand_derivatives(O; ...)\nexpand_derivatives(O, simplify; occurrences)\n\n\nExpands derivatives within a symbolic expression O.\n\nThis function recursively traverses a symbolic expression, applying the chain rule and other derivative rules to expand any derivatives it encounters.\n\nArguments\n\nO::Symbolic: The symbolic expression to expand.\nsimplify::Bool=false: Whether to simplify the resulting expression using SymbolicUtils.simplify.\noccurrences=nothing: Information about the occurrences of the independent variable in the argument of the derivative. This is used internally for optimization purposes.\n\nExamples\n\njulia> @variables x y z k;\n\njulia> f = k*(abs(x-y)/y-z)^2\nk*((abs(x - y) / y - z)^2)\n\njulia> Dx = Differential(x) # Differentiate wrt x\n(::Differential) (generic function with 2 methods)\n\njulia> dfx = expand_derivatives(Dx(f))\n(k*((2abs(x - y)) / y - 2z)*IfElse.ifelse(signbit(x - y), -1, 1)) / y\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"note: Note\nFor symbolic differentiation, all registered functions in the symbolic expression need a registered derivative. For more information, see the function registration page.","category":"page"},{"location":"manual/derivatives/#High-Level-Differentiation-Functions","page":"Derivatives and Differentials","title":"High-Level Differentiation Functions","text":"","category":"section"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"The following functions are not exported and thus must be accessed in a namespaced way, i.e. Symbolics.jacobian.","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"Symbolics.derivative\nSymbolics.jacobian\nSymbolics.sparsejacobian\nSymbolics.sparsejacobian_vals\nSymbolics.gradient\nSymbolics.hessian\nSymbolics.sparsehessian\nSymbolics.sparsehessian_vals","category":"page"},{"location":"manual/derivatives/#Symbolics.derivative","page":"Derivatives and Differentials","title":"Symbolics.derivative","text":"derivative(O, var; simplify)\n\n\nA helper function for computing the derivative of the expression O with respect to var.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.jacobian","page":"Derivatives and Differentials","title":"Symbolics.jacobian","text":"jacobian(ops, vars; simplify, scalarize)\n\n\nA helper function for computing the Jacobian of an array of expressions with respect to an array of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.sparsejacobian","page":"Derivatives and Differentials","title":"Symbolics.sparsejacobian","text":"sparsejacobian(ops, vars; simplify)\n\n\nA helper function for computing the sparse Jacobian of an array of expressions with respect to an array of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.sparsejacobian_vals","page":"Derivatives and Differentials","title":"Symbolics.sparsejacobian_vals","text":"sparsejacobian_vals(ops, vars, I, J; simplify)\n\n\nA helper function for computing the values of the sparse Jacobian of an array of expressions with respect to an array of variable expressions given the sparsity structure.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.gradient","page":"Derivatives and Differentials","title":"Symbolics.gradient","text":"gradient(O, vars; simplify)\n\n\nA helper function for computing the gradient of the expression O with respect to an array of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.hessian","page":"Derivatives and Differentials","title":"Symbolics.hessian","text":"hessian(O, vars; simplify)\n\n\nA helper function for computing the Hessian of the expression O with respect to an array of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.sparsehessian","page":"Derivatives and Differentials","title":"Symbolics.sparsehessian","text":"sparsehessian(op, vars; simplify, full)\n\n\nA helper function for computing the sparse Hessian of an expression with respect to an array of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.sparsehessian_vals","page":"Derivatives and Differentials","title":"Symbolics.sparsehessian_vals","text":"sparsehessian_vals(op, vars, I, J; simplify)\n\n\nA helper function for computing the values of the sparse Hessian of an expression with respect to an array of variable expressions given the sparsity structure.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Adding-Analytical-Derivatives","page":"Derivatives and Differentials","title":"Adding Analytical Derivatives","text":"","category":"section"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"There are many derivatives pre-defined by DiffRules.jl. For example,","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"using Symbolics\n@variables x y z\nf(x,y,z) = x^2 + sin(x+y) - z","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"f automatically has the derivatives defined via the tracing mechanism. It will do this by directly building the internals of your function and differentiating that.","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"However, often you may want to define your own derivatives so that way automatic Jacobian etc. calculations can utilize this information. This can allow for more succinct versions of the derivatives to be calculated to scale to larger systems. You can define derivatives for your function via the dispatch:","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"# `N` arguments are accepted by the relevant method of `my_function`\nSymbolics.derivative(::typeof(my_function), args::NTuple{N,Any}, ::Val{i})","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"where i means that it's the derivative with respect to the ith argument. args is the array of arguments, so, for example, if your function is f(x,t), then args = [x,t]. You should return an Term for the derivative of your function.","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"For example, sin(t)'s derivative (by t) is given by the following:","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"Symbolics.derivative(::typeof(sin), args::NTuple{1,Any}, ::Val{1}) = cos(args[1])","category":"page"},{"location":"manual/taylor/#Taylor-Series","page":"Taylor Series","title":"Taylor Series","text":"","category":"section"},{"location":"manual/taylor/","page":"Taylor Series","title":"Taylor Series","text":"For a real example of how to use the Taylor series functionality, see this tutorial.","category":"page"},{"location":"manual/taylor/","page":"Taylor Series","title":"Taylor Series","text":"series\ntaylor\ntaylor_coeff","category":"page"},{"location":"manual/taylor/#Symbolics.series","page":"Taylor Series","title":"Symbolics.series","text":"series(cs, x, [x0=0,], ns=0:length(cs)-1)\n\nReturn the power series in x around x0 to the powers ns with coefficients cs.\n\nseries(y, x, [x0=0,] ns)\n\nReturn the power series in x around x0 to the powers ns with coefficients automatically created from the variable y.\n\nExamples\n\njulia> @variables x y[0:3] z\n3-element Vector{Any}:\n x\n y[0:3]\n z\n\njulia> series(y, x, 2)\ny[0] + (-2 + x)*y[1] + ((-2 + x)^2)*y[2] + ((-2 + x)^3)*y[3]\n\njulia> series(z, x, 2, 0:3)\nz[0] + (-2 + x)*z[1] + ((-2 + x)^2)*z[2] + ((-2 + x)^3)*z[3]\n\n\n\n\n\n","category":"function"},{"location":"manual/taylor/#Symbolics.taylor","page":"Taylor Series","title":"Symbolics.taylor","text":"taylor(f, x, [x0=0,] n; rationalize=true)\n\nCalculate the n-th order term(s) in the Taylor series of f around x = x0. If rationalize, float coefficients are approximated as rational numbers (this can produce unexpected results for irrational numbers, for example).\n\nExamples\n\njulia> @variables x\n1-element Vector{Num}:\n x\n\njulia> taylor(exp(x), x, 0:3)\n1 + x + (1//2)*(x^2) + (1//6)*(x^3)\n\njulia> taylor(exp(x), x, 0:3; rationalize=false)\n1.0 + x + 0.5(x^2) + 0.16666666666666666(x^3)\n\njulia> taylor(√(x), x, 1, 0:3)\n1 + (1//2)*(-1 + x) - (1//8)*((-1 + x)^2) + (1//16)*((-1 + x)^3)\n\njulia> isequal(taylor(exp(im*x), x, 0:5), taylor(exp(im*x), x, 0:5))\ntrue\n\n\n\n\n\n","category":"function"},{"location":"manual/taylor/#Symbolics.taylor_coeff","page":"Taylor Series","title":"Symbolics.taylor_coeff","text":"taylor_coeff(f, x[, n]; rationalize=true)\n\nCalculate the n-th order coefficient(s) in the Taylor series of f around x = 0.\n\nExamples\n\njulia> @variables x y\n2-element Vector{Num}:\n x\n y\n\njulia> taylor_coeff(series(y, x, 0:5), x, 0:2:4)\n3-element Vector{Num}:\n y[0]\n y[2]\n y[4]\n\n\n\n\n\n","category":"function"},{"location":"#Symbolics.jl","page":"Home","title":"Symbolics.jl","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Symbolics.jl is a fast and modern Computer Algebra System (CAS) for a fast and modern programming language (Julia). The goal is to have a high-performance and parallelized symbolic algebra system that is directly extendable in the same language as that of the users.","category":"page"},{"location":"#Installation","page":"Home","title":"Installation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"To install Symbolics.jl, use the Julia package manager:","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg\nPkg.add(\"Symbolics\")","category":"page"},{"location":"#Citation","page":"Home","title":"Citation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"If you use Symbolics.jl, please cite this paper","category":"page"},{"location":"","page":"Home","title":"Home","text":"@article{gowda2021high,\n title={High-performance symbolic-numerics via multiple dispatch},\n author={Gowda, Shashi and Ma, Yingbo and Cheli, Alessandro and Gwozdz, Maja and Shah, Viral B and Edelman, Alan and Rackauckas, Christopher},\n journal={arXiv preprint arXiv:2105.03949},\n year={2021}\n}","category":"page"},{"location":"#Feature-Summary","page":"Home","title":"Feature Summary","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Because Symbolics.jl is built into the Julia language and works with its dispatches, generic functions in Base Julia will work with symbolic expressions! Make matrices of symbolic expressions and multiply them: it will just work. Take the LU-factorization. Etc. Thus, see the Julia Documentation for a large list of functionality available in Symbolics.jl.","category":"page"},{"location":"","page":"Home","title":"Home","text":"A general list of the features is:","category":"page"},{"location":"","page":"Home","title":"Home","text":"Symbolic arithmetic with type information and multiple dispatch\nSymbolic polynomials and trigonometric functions\nPattern matching, simplification and substitution\nDifferentiation\nSymbolic linear algebra (factorizations, inversion, determinants, eigencomputations, etc.)\nDiscrete math (representations of summations, products, binomial coefficients, etc.)\nLogical and Boolean expressions\nSymbolic equation solving and conversion to arbitrary precision\nSupport for non-standard algebras (non-commutative symbols and customizable rulesets)\nSpecial functions (list provided by SpecialFunctions.jl)\nAutomatic conversion of Julia code to symbolic code\nGeneration of (high performance and parallel) functions from symbolic expressions\nFast automated sparsity detection and generation of sparse Jacobians and Hessians","category":"page"},{"location":"","page":"Home","title":"Home","text":"and much more.","category":"page"},{"location":"#Extension-Packages","page":"Home","title":"Extension Packages","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Below is a list of known extension packages. If you would like your package to be listed here, feel free to open a pull request!","category":"page"},{"location":"","page":"Home","title":"Home","text":"ModelingToolkit.jl: Symbolic representations of common numerical systems\nOrdinary differential equations\nStochastic differential equations\nPartial differential equations\nNonlinear systems\nOptimization problems\nOptimal Control\nCausal and acausal modeling (Simulink/Modelica)\nAutomated model transformation, simplification, and composition\nCatalyst.jl: Symbolic representations of chemical reactions\nSymbolically build and represent large systems of chemical reactions\nGenerate code for ODEs, SDEs, continuous-time Markov Chains, and more\nSimulate the models using the SciML ecosystem with O(1) Gillespie methods\nDataDrivenDiffEq.jl: Automatic identification of equations from data\nAutomated construction of ODEs and DAEs from data\nRepresentations of Koopman operators and Dynamic Mode Decomposition (DMD)\nSymbolicRegression.jl: Distributed High-Performance symbolic regression\nParallelized generic algorithms for finding equations from data\nPareto frontier-based scoring\nReversePropagation.jl: Source-to-source reverse mode automatic differentiation\nAutomated tracing of code and construction of backpropagation equations\nComposes with symbolic transformation and simplification functionality","category":"page"},{"location":"#Reproducibility","page":"Home","title":"Reproducibility","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"
The documentation of this SciML package was built using these direct dependencies,","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg # hide\nPkg.status() # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"
and using this machine and Julia version.","category":"page"},{"location":"","page":"Home","title":"Home","text":"using InteractiveUtils # hide\nversioninfo() # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"
A more complete overview of all dependencies and their versions is also provided.","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg # hide\nPkg.status(; mode = PKGMODE_MANIFEST) # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"using TOML\nusing Markdown\nversion = TOML.parse(read(\"../../Project.toml\", String))[\"version\"]\nname = TOML.parse(read(\"../../Project.toml\", String))[\"name\"]\nlink_manifest = \"https://github.com/SciML/\" * name * \".jl/tree/gh-pages/v\" * version *\n \"/assets/Manifest.toml\"\nlink_project = \"https://github.com/SciML/\" * name * \".jl/tree/gh-pages/v\" * version *\n \"/assets/Project.toml\"\nMarkdown.parse(\"\"\"You can also download the\n[manifest]($link_manifest)\nfile and the\n[project]($link_project)\nfile.\n\"\"\")","category":"page"}] +[{"location":"manual/faq/#Frequently-Asked-Questions","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"","category":"section"},{"location":"manual/faq/#Limits-of-Symbolic-Computation","page":"Frequently Asked Questions","title":"Limits of Symbolic Computation","text":"","category":"section"},{"location":"manual/faq/#Transforming-my-function-to-a-symbolic-equation-has-failed.-What-do-I-do?","page":"Frequently Asked Questions","title":"Transforming my function to a symbolic equation has failed. What do I do?","text":"","category":"section"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"If you see the error:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"ERROR: TypeError: non-boolean (Num) used in boolean context","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"this is likely coming from an algorithm which cannot be traced into a purely symbolic algorithm. Many numerical solvers, for instance, have this property. It shows up when you're doing something like if x < tol. If x is a number, then this is true or false. If x is a symbol, then it's x < tol, so Julia just cannot know how many iterations to do and throws an error.","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"This shows up in adaptive algorithms, for example:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"function factorial(x)\n out = x\n while x > 1\n x -= 1\n out *= x\n end\n out\nend","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"The number of iterations this algorithm runs for is dependent on the value of x, and so there is no static representation of the algorithm. If x is 5, then it's out = x*(x-1)*(x-2)*(x-3)*(x-4), while if x is 3, then it's out = x*(x-1)*(x-2). It should thus be no surprise that:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"using Symbolics\n@variables x\ntry\n factorial(x)\ncatch e\n e\nend","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"fails. It's not that there is anything wrong with this code, but it's not going to work because fundamentally this is not a symbolically-representable algorithm.","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"The space of algorithms which can be turned into symbolic algorithms is what we call quasi-static, that is, there is a way to represent the algorithm as static. Loops are allowed, but the amount of loop iterations should not require that you know the value of the symbol x. If the algorithm is quasi-static, then Symbolics.jl tracing will produce the static form of the code, unrolling the operations, and generating a flat representation of the algorithm.","category":"page"},{"location":"manual/faq/#What-can-be-done?","page":"Frequently Asked Questions","title":"What can be done?","text":"","category":"section"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"If you need to represent this function f symbolically, then you'll need to make sure it's not traced and instead is directly represented in the underlying computational graph. Just like how sqrt(x) symbolically does not try to represent the underlying algorithm, this must be done to your f. This is done by doing @register_symbolic f(x). If you have to define things like derivatives to f, then the function registration documentation.","category":"page"},{"location":"manual/faq/#Equality-and-set-membership-tests","page":"Frequently Asked Questions","title":"Equality and set membership tests","text":"","category":"section"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"Comparing symbols with == produces a symbolic equality, not a Bool. To produce a Bool, call isequal.","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"To test if a symbol is part of a collection of symbols, i.e., a vector, either create a Set and use in, e.g.","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"try \n x in [x]\ncatch e\n e\nend","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"x in Set([x])","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"any(isequal(x), [x])","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"If == is used instead, you will receive TypeError: non-boolean (Num) used in boolean context. What this error is telling you is that the symbolic x == y expression is being used where a Bool is required, such as if x == y, and since the symbolic expression is held lazily this will error because the appropriate branch cannot be selected (since x == y is unknown for arbitrary symbolic values!). This is why the check isequal(x,y) is required, since this is a non-lazy check of whether the symbol x is always equal to the symbol y, rather than an expression of whether x and y currently have the same value.","category":"page"},{"location":"manual/faq/#Understanding-the-Difference-Between-the-Julia-Variable-and-the-Symbolic-Variable","page":"Frequently Asked Questions","title":"Understanding the Difference Between the Julia Variable and the Symbolic Variable","text":"","category":"section"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"In the most basic usage of Symbolics, the name of the Julia variable and the symbolic variable are the same. For example, when we do:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"@variables a","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"the name of the symbolic variable is a and same with the Julia variable. However, we can de-couple these by setting a to a new symbolic variable, for example:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"b = only(@variables(a))","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"Now the Julia variable b refers to the variable named a. However, the downside of this current approach is that it requires that the user writing the script knows the name a that they want to place to the variable. But what if for example we needed to get the variable's name from a file?","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"To do this, one can interpolate a symbol into the @variables macro using $. For example:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"a = :c\nb = only(@variables($a))","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"In this example, @variables($a) created a variable named c, and set this variable to b.","category":"page"},{"location":"comparison/#Comparison-of-Julia's-Symbolics.jl-vs-SymPy-for-Symbolic-Computation","page":"Comparison Against SymPy","title":"Comparison of Julia's Symbolics.jl vs SymPy for Symbolic Computation","text":"","category":"section"},{"location":"comparison/","page":"Comparison Against SymPy","title":"Comparison Against SymPy","text":"Symbolics.jl is a symbolic modeling language for Julia, built in Julia. Its goal is very different from Sympy: it was made to support symbolic-numerics, the combination of symbolic computing with numerical methods to allow for extreme performance computing that would not be possible without modifying the model. Because of this, Symbolics.jl excels in many areas due to purposeful design decisions:","category":"page"},{"location":"comparison/","page":"Comparison Against SymPy","title":"Comparison Against SymPy","text":"Performance: Symbolics.jl is built in Julia, whereas SymPy was built in Python. Thus, the performance bar for Symbolics.jl is much higher. Symbolics.jl started because SymPy was far too slow and SymEngine was far too inflexible for the projects they were doing. Performance is key to Symbolics.jl. If you find any performance issues, please file an issue.\nbuild_function: lambdify is “fine” for some people, but if you're building a super fast MPI-enabled Julia/C/Fortran simulation code, having a function that hits the Python interpreter is less than optimal. By default, build_function builds fast JIT-compiled functions due to being in Julia. However, it has support for things like static arrays, non-allocating functions via mutation, fast functions on sparse matrices and arrays of arrays, etc.: all core details of doing high performance computing.\nParallelism: Symbolics.jl has pervasive parallelism. The symbolic simplification via SymbolicUtils.jl has built-in parallelism, Symbolics.jl builds functions that parallelize across threads. Symbolics.jl is compatible with GPU libraries like CUDA.jl.\nExtensible: Symbolics.jl and its underlying tools are written in pure Julia. Want to add new or better simplification rules? Add some Julia code! Need to add new derivatives? Add some Julia code! You get the picture. Breaking down these barriers makes it easier for the user to tailor the program to their needs and accelerates the development of the library.\nDeep integration with the Julia ecosystem: Symbolics.jl's integration with neural networks is not the only thing that's deep. Symbolics.jl is built with the same philosophy as other SciML packages, eschewing “monorepos” for a distributed development approach that ties together the work of many developers. The differentiation parts utilize tools from automatic differentiation libraries, all linear algebra functionality comes from tracing Julia Base itself, symbolic rewriting (simplification and substitution) comes from SymbolicUtils.jl, parallelism comes from Julia Base libraries, Dagger.jl, etc. SciML Tools like DataDrivenDiffEq.jl can reconstruct symbolic expressions from neural networks and data, while NeuralPDE.jl can automatically solve partial differential equations from symbolic descriptions using physics-informed neural networks. The list keeps going. All told, by design Symbolics.jl's development moves fast because it's effectively using the work of hundreds of Julia developers, allowing it to grow fast.\nWhile Symbolics.jl has many features missing from SymPy, it does not superset SymPy's functionality. For a list of missing features, see this issue.","category":"page"},{"location":"manual/groebner/#Groebner-bases","page":"Groebner bases","title":"Groebner bases","text":"","category":"section"},{"location":"manual/groebner/","page":"Groebner bases","title":"Groebner bases","text":"Groebner bases use the implementation of the F4 algorithm from Groebner.jl package as its backend. We refer to the documentation of Groebner.jl, which lists some implementations details and possible use-cases of Groebner bases.","category":"page"},{"location":"manual/groebner/","page":"Groebner bases","title":"Groebner bases","text":"groebner_basis","category":"page"},{"location":"manual/groebner/#Symbolics.groebner_basis","page":"Groebner bases","title":"Symbolics.groebner_basis","text":"groebner_basis(polynomials)\n\nComputes a Groebner basis of the ideal generated by the given polynomials.\n\nThis function requires a Groebner bases backend (such as Groebner.jl) to be loaded.\n\n\n\n\n\n","category":"function"},{"location":"tutorials/perturbation/#perturb_alg","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"","category":"section"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Symbolics.jl is a fast and modern Computer Algebra System (CAS) written in Julia. It is an integral part of the SciML ecosystem of differential equation solvers and scientific machine learning packages. While Symbolics.jl is primarily designed for modern scientific computing (e.g. automatic differentiation and machine learning), it is also a powerful CAS that can be used for classic scientific computing. One such application is using perturbation theory to solve algebraic and differential equations.","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Perturbation methods are a collection of techniques to solve hard problems that generally don't have a closed solution, but depend on a tunable parameter and have closed or easy solutions for some values of this parameter. The main idea is to assume a solution that is a power series in the tunable parameter (say ϵ), such that ϵ = 0 corresponds to an easy solution, and then solve iteratively for higher-order corrections.","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"The hallmark of perturbation theory is the generation of long and convoluted intermediate equations by this process. These are subjected to algorithmic and mechanical manipulations, which makes perturbation methods an excellent fit for a CAS. In fact, CAS software have been used for perturbation calculations since the early 1970s.","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"This tutorial shows how to mix symbolic manipulations and numerical methods to solve algebraic equations with perturbation theory. Another tutorial applies it to differential equations.","category":"page"},{"location":"tutorials/perturbation/#Solving-the-quintic-equation","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Solving the quintic equation","text":"","category":"section"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"The “hello world!” analog of perturbation problems is to find a real solution x to the quintic (fifth-order) equation","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"using Symbolics\n@variables x\nquintic = x^5 + x ~ 1","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"According to Abel's theorem, a general quintic equation does not have a closed form solution. But we can easily solve it numerically using Newton's method (here implemented for simplicity, and not performance):","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"function solve_newton(eq, x, x₀; abstol=1e-8, maxiters=50)\n # symbolic expressions for f(x) and f′(x)\n f = eq.lhs - eq.rhs # want to find root of f(x)\n f′ = Symbolics.derivative(f, x)\n\n xₙ = x₀ # numerical value of the initial guess\n for i = 1:maxiters\n # calculate new guess by numerically evaluating symbolic expression at previous guess\n xₙ₊₁ = substitute(x - f / f′, x => xₙ)\n if abs(xₙ₊₁ - xₙ) < abstol\n return xₙ₊₁ # converged\n else\n xₙ = xₙ₊₁\n end\n end\n error(\"Newton's method failed to converge\")\nend\n\nx_newton = solve_newton(quintic, x, 1.0)\nprintln(\"Newton's method solution: x = \", x_newton)","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"To solve the same problem with perturbation theory, we must introduce an expansion variable ϵ in the equation:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"@variables ϵ # expansion variable\nquintic = x^5 + ϵ*x ~ 1","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"If ϵ = 1, we get our original problem. With ϵ = 0, the problem transforms to the easy quintic equation x^5 = 1 with the trivial real solution x = 1 (and four complex solutions which we ignore). Next, expand x as a power series in ϵ:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"x_coeffs, = @variables a[0:7] # create Taylor series coefficients\nx_taylor = series(x_coeffs, ϵ) # expand x in a power series in ϵ","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Then insert this into the quintic equation and expand it, too, to the same order:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"quintic_taylor = substitute(quintic, x => x_taylor)\nquintic_taylor = taylor(quintic_taylor, ϵ, 0:7)","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"This messy equation must hold for each power of ϵ, so we can separate it into one nicer equation per order:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"quintic_eqs = taylor_coeff(quintic_taylor, ϵ, 0:7)\nquintic_eqs[1:5] # for readability, show only 5 shortest equations","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"These equations show three important features of perturbation theory:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"The 0-th order equation is trivial in x_0: here x_0^5 = 1 has the trivial real solution x_0 = 1.\nThe n-th order equation is linear in x_n (except the trivial 0-th order equation).\nThe equations are triangular in x_n: the n-th order equation can be solved for x_n given only x_m for mn.","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"This structure is what makes the perturbation theory so attractive: we can start with the trivial solution x_0 = 1, then linearly solve for x_n order-by-order and substitute in the solutions of x_m for mn obtained so far.","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Here is a simple function that uses this cascading strategy to solve such a set of equations eqs for the variables xs, given a solution x₀ of the first equation eqs[begin]:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"function solve_cascade(eqs, xs, x₀, ϵ)\n sol = Dict(xs[begin] => x₀) # store solutions in a map\n\n # verify that x₀ is a solution of the first equation\n eq0 = substitute(eqs[1], sol)\n isequal(eq0.lhs, eq0.rhs) || error(\"$sol does not solve $(eqs[1])\")\n\n # solve remaining equations sequentially\n for i in 2:length(eqs)\n eq = substitute(eqs[i], sol) # insert previous solutions\n x = xs[begin+i-1] # need not be 1-indexed\n xsol = Symbolics.symbolic_linear_solve(eq, x) # solve current equation\n sol = merge(sol, Dict(x => xsol)) # store solution\n end\n\n return sol\nend","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Let us solve our order-separated quintics for the coefficients, and substitute them into the full series for x:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"x_coeffs_sol = solve_cascade(quintic_eqs, x_coeffs, 1, ϵ)\nx_pert = substitute(x_taylor, x_coeffs_sol)","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"The n-th order solution of our original quintic equation is the sum up to the ϵ^n-th order term, evaluated at ϵ=1:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"for n in 0:7\n x_pert_sol = substitute(taylor(x_pert, ϵ, 0:n), ϵ => 1)\n println(\"$n-th order solution: x = $x_pert_sol = $(x_pert_sol * 1.0)\")\nend","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"This is close to the solution from Newton's method!","category":"page"},{"location":"tutorials/perturbation/#Solving-Kepler's-Equation","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Solving Kepler's Equation","text":"","category":"section"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Historically, perturbation methods were first invented to calculate orbits of the Moon and the planets. In homage to this history, our second example is to solve Kepler's equation, which is central to solving two-body Keplerian orbits:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"@variables e E M\nkepler = E - e * sin(E) ~ M","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"We want to solve for the eccentric anomaly E given the eccentricity e and mean anomaly M. This is also easy with Newton's method. With Earth's eccentricity e = 001671 and M = pi2:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"vals_earth = Dict(e => 0.01671, M => π/2)\nE_newton = solve_newton(substitute(kepler, vals_earth), E, π/2)\nprintln(\"Newton's method solution: E = \", E_newton)","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Next, let us solve the same problem with the perturbative method. It is most common to expand E as a series in M. Repeating the procedure from the quintic example, we get these equations:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"E_taylor = series(E, M, 0:5) # this auto-creates coefficients E[0:5]\nE_coeffs = taylor_coeff(E_taylor, M) # get a handle to the coefficients\nkepler_eqs = taylor_coeff(substitute(kepler, E => E_taylor), M, 0:5)\nkepler_eqs[1:4] # for readability","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"The trivial 0-th order solution (when M=0) is E_0=0. This gives this full perturbative solution:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"E_coeffs_sol = solve_cascade(kepler_eqs, E_coeffs, 0, M)\nE_pert = substitute(E_taylor, E_coeffs_sol)","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Numerically, the result again converges to that from Newton's method:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"for n in 0:5\n println(\"$n-th order solution: E = \", substitute(taylor(E_pert, M, 0:n), vals_earth))\nend","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"But unlike Newtons method, this example shows how perturbation theory also gives us the powerful symbolic series solution for E (before numbers for e and M are inserted). Our series matches this result from Wikipedia:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"E_wiki = 1/(1-e)*M - e/(1-e)^4*M^3/factorial(3) + (9e^2+e)/(1-e)^7*M^5/factorial(5)","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Alternatively, we can expand E in e instead of M, giving the solution (the trivial solution when e = 0 is E_0=M):","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"E_taylor′ = series(E, e, 0:5)\nE_coeffs′ = taylor_coeff(E_taylor′, e)\nkepler_eqs′ = taylor_coeff(substitute(kepler, E => E_taylor′), e, 0:5)\nE_coeffs_sol′ = solve_cascade(kepler_eqs′, E_coeffs′, M, e)\nE_pert′ = substitute(E_taylor′, E_coeffs_sol′)","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"This looks very different from our first series E_pert. If they are the same, we should get 0 if we subtract and expand both as multivariate Taylor series in (eM). Indeed:","category":"page"},{"location":"tutorials/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"@assert taylor(taylor(E_pert′ - E_pert, e, 0:4), M, 0:4) == 0 # use this as a test # hide\ntaylor(taylor(E_pert′ - E_pert, e, 0:4), M, 0:4)","category":"page"},{"location":"manual/types/#Supported-types-and-dispatch-in-Symbolics","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"","category":"section"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"There is a tension between types as a representation of expression trees and types that are a subtype of types already present in Julia.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"We want to be able to deal with expression trees in a unified way and not constrain expression trees themselves to be under an abstract type in Julia's type hierarchy. (For example, if we said that all expression trees are subtype of Real, then we couldn't represent array operations using the same expression tree.). But we also want to be able to pass in expression trees into places in existing code that accept Real values.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"We accomplish this by wrapping expression trees in a simple wrapper type which is a subtype of our desired abstract type. For example, we wrap expression trees in the type Num which is a subtype of Real to make it behave like a Real number.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"The methods on Num objects are forwarded to the wrapped expression tree. And care is taken so that an expression tree never internally contains Num – this is both for performance and separation of concerns.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"User-facing APIs in Symbolics always take wrapped objects like Num, they are then internally unwrapped for expression tree manipulation.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"Due to it requiring such wrappers, we only fully support a limited number of types as both the types of expression trees and the type as Julia sees them.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"These types are","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"Real numbers (wrapped using Num)\ncomplex numbers (stored as Complex{Num} where Complex is from Base Julia)\narrays of Real and complex numbers (wrapped using Arr, so Arr{Num} or Arr{Complex{Num}})","category":"page"},{"location":"manual/types/#@variables-and-types","page":"Supported types and dispatch in Symbolics","title":"@variables and types","text":"","category":"section"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"Use the syntax @variables x::T to create a symbol named x of symbolic type T. If T is a subtype of any of the above listed types which support a wrapper, the resulting variable will be wrapped in that type. As seen in the examples below, x,z,X,Z all have a suitable wrapper type. Hence, their types are shown. However, s being of symbolic type String does not have a corresponding wrapper supported by Symbolics, and hence, it returns a Sym{String} object. This is the trivial expression tree of a single variable without a wrapper, and is not a subtype of String or AbstractString.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"using Symbolics\n@variables x::Real z::Complex{Real} (X::Real)[1:10, 1:10] (Z::Complex{Real})[1:10] s::String","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"typeof(x)","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"typeof(z)","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"typeof(X)","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"typeof(Z)","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"typeof(s)","category":"page"},{"location":"manual/parsing/#Parsing-Julia-Expressions-to-Symbolic-Expressions","page":"Parsing Julia Expressions to Symbolic Expressions","title":"Parsing Julia Expressions to Symbolic Expressions","text":"","category":"section"},{"location":"manual/parsing/","page":"Parsing Julia Expressions to Symbolic Expressions","title":"Parsing Julia Expressions to Symbolic Expressions","text":"Julia expressions such as :(y - x) are fundamentally different from symbolic expressions, as they do not have an algebra defined on them. Thus, it can be very helpful when building domain-specific languages (DSLs) and parsing files to convert from Julia expressions to Symbolics.jl expressions for further manipulation. Towards this end is the parse_expr_to_symbolic which performs the parsing.","category":"page"},{"location":"manual/parsing/","page":"Parsing Julia Expressions to Symbolic Expressions","title":"Parsing Julia Expressions to Symbolic Expressions","text":"warning: Warning\nTake the limitations mentioned in the parse_expr_to_symbolic docstrings seriously! Because Julia expressions contain no symbolic metadata, there is limited information and thus the parsing requires heuristics to work.","category":"page"},{"location":"manual/parsing/","page":"Parsing Julia Expressions to Symbolic Expressions","title":"Parsing Julia Expressions to Symbolic Expressions","text":"parse_expr_to_symbolic\n@parse_expr_to_symbolic","category":"page"},{"location":"manual/parsing/#Symbolics.parse_expr_to_symbolic","page":"Parsing Julia Expressions to Symbolic Expressions","title":"Symbolics.parse_expr_to_symbolic","text":"parse_expr_to_symbolic(ex, mod::Module)\n\nApplies the parse_expr_to_symbolic function in the current module, i.e. parse_expr_to_symbolic(ex, mod) where mod is the module of the function caller.\n\nArguments\n\nex: the expression to parse\nmod: the module to apply the parsing in. See the limitations section for details.\n\nExample\n\nex = :(y(t) ~ x(t))\nparse_expr_to_symbolic(ex,Main) # gives the symbolic expression `y(t) ~ x(t)` in empty Main\n\n# Now do a whole system\n\nex = [:(y ~ x)\n :(y ~ -2x + 3 / z)\n :(z ~ 2)]\neqs = parse_expr_to_symbolic.(ex, (Main,))\n\n@variables x y z\nex = [y ~ x\n y ~ -2x + 3 / z\n z ~ 2]\nall(isequal.(eqs,ex)) # true\n\nLimitations\n\nSymbolic-ness Tied to Environment Definitions\n\nThe parsing to a symbolic expression has to be able to recognize the difference between functions, numbers, and globals defined within one's Julia environment and those that are to be made symbolic. The way this functionality handles this problem is that it does not define anything as symbolic that is already defined in the chosen mod module. For example, f(x,y) will have f as non-symbolic if the function f (named f) is defined in mod, i.e. if isdefined(mod,:f) is true. When the symbol is defined, it will be replaced by its value. Notably, this means that the parsing behavior changes depending on the environment that it is applied.\n\nFor example:\n\nparse_expr_to_symbolic(:(x - y),@__MODULE__) # x - y\nx = 2.0\nparse_expr_to_symbolic(:(x - y),@__MODULE__) # 2.0 - y\n\nThis is required to detect that standard functions like - are functions instead of symbolic symbols. For safety, one should create anonymous modules or other sub-environments to ensure no stray variables are defined.\n\nMetadata is Blank\n\nBecause all the variables defined by the expressions are not defined with the standard @variables, there is no metadata that is or can be associated with any of the generated variables. Instead, they all have blank metadata, but are defined in the Real domain. Thus, the variables which come out of this parsing may not evaluate as equal to a symbolic variable defined elsewhere.\n\n\n\n\n\n","category":"function"},{"location":"manual/variables/#Variable-and-Equation-Types","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"","category":"section"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Symbolics IR mirrors the Julia AST but allows for easy mathematical manipulation by itself following mathematical semantics. The base of the IR is the Sym type, which defines a symbolic variable. Registered (mathematical) functions on Syms (or iscall objects) return an expression that iscall. For example, op1 = x+y is one symbolic object and op2 = 2z is another, and so op1*op2 is another tree object. Then, at the top, an Equation, normally written as op1 ~ op2, defines the symbolic equality between two operations.","category":"page"},{"location":"manual/variables/#Types","page":"Variable and Equation Types","title":"Types","text":"","category":"section"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Sym, Term, and FnType are from SymbolicUtils.jl. Note that in Symbolics, we always use Sym{Real}, Term{Real}, and FnType{Tuple{Any}, Real}. To get the arguments of an iscall object, use arguments(t::Term), and to get the operation, use operation(t::Term). However, note that one should never dispatch on Term or test isa Term. Instead, one needs to use SymbolicUtils.iscall to check if arguments and operation is defined.","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"@variables\nSymbolics.variable\nSymbolics.variables\nEquation\nBase.:~(::Num, ::Num)","category":"page"},{"location":"manual/variables/#Symbolics.@variables","page":"Variable and Equation Types","title":"Symbolics.@variables","text":"Define one or more unknown variables.\n\n@variables t α σ(..) β[1:2]\n@variables w(..) x(t) y z(t, α, x)\n\nexpr = β[1]* x + y^α + σ(3) * (z - t) - β[2] * w(t - 1)\n\n(..) signifies that the value should be left uncalled.\n\nSymbolics supports creating variables that denote an array of some size.\n\njulia> @variables x[1:3]\n1-element Vector{Symbolics.Arr{Num, 1}}:\n x[1:3]\n\njulia> @variables y[1:3, 1:6] # support for tensors\n1-element Vector{Symbolics.Arr{Num, 2}}:\n y[1:3,1:6]\n\njulia> @variables t z(t)[1:3] # also works for dependent variables\n2-element Vector{Any}:\n t\n (z(t))[1:3]\n\nA symbol or expression that represents an array can be turned into an array of symbols or expressions using the scalarize function.\n\njulia> Symbolics.scalarize(z)\n3-element Vector{Num}:\n (z(t))[1]\n (z(t))[2]\n (z(t))[3]\n\nNote that @variables returns a vector of all the defined variables.\n\n@variables can also take runtime symbol values by the $ interpolation operator, and in this case, @variables doesn't automatically assign the value, instead, it only returns a vector of symbolic variables. All the rest of the syntax also applies here.\n\njulia> a, b, c = :runtime_symbol_value, :value_b, :value_c\n(:runtime_symbol_value, :value_b, :value_c)\n\njulia> vars = @variables t $a $b(t) $c(t)[1:3]\n4-element Vector{Any}:\n t\n runtime_symbol_value\n value_b(t)\n (value_c(t))[1:3]\n\njulia> (t, a, b, c)\n(t, :runtime_symbol_value, :value_b, :value_c)\n\n\n\n\n\n","category":"macro"},{"location":"manual/variables/#Symbolics.variable","page":"Variable and Equation Types","title":"Symbolics.variable","text":"variable(name::Symbol, idx::Integer...; T=Real)\n\nCreate a variable with the given name along with subscripted indices with the symtype=T. When T=FnType, it creates a symbolic function.\n\njulia> Symbolics.variable(:x, 4, 2, 0)\nx₄ˏ₂ˏ₀\n\njulia> Symbolics.variable(:x, 4, 2, 0, T=Symbolics.FnType)\nx₄ˏ₂ˏ₀⋆\n\nAlso see variables.\n\n\n\n\n\n","category":"function"},{"location":"manual/variables/#Symbolics.variables","page":"Variable and Equation Types","title":"Symbolics.variables","text":"variables(name::Symbol, indices...)\n\nCreate a multi-dimensional array of individual variables named with subscript notation. Use @variables instead to create symbolic array variables (as opposed to array of variables). See variable to create one variable with subscripts.\n\njulia> Symbolics.variables(:x, 1:3, 3:6)\n3×4 Matrix{Num}:\n x₁ˏ₃ x₁ˏ₄ x₁ˏ₅ x₁ˏ₆\n x₂ˏ₃ x₂ˏ₄ x₂ˏ₅ x₂ˏ₆\n x₃ˏ₃ x₃ˏ₄ x₃ˏ₅ x₃ˏ₆\n\n\n\n\n\n","category":"function"},{"location":"manual/variables/#Symbolics.Equation","page":"Variable and Equation Types","title":"Symbolics.Equation","text":"struct Equation\n\nAn equality relationship between two expressions.\n\nFields\n\nlhs: The expression on the left-hand side of the equation.\nrhs: The expression on the right-hand side of the equation.\n\n\n\n\n\n","category":"type"},{"location":"manual/variables/#Base.:~-Tuple{Num, Num}","page":"Variable and Equation Types","title":"Base.:~","text":"~(lhs, rhs) -> Any\n\n\nCreate an Equation out of two Num instances, or an Num and a Number.\n\nExamples\n\njulia> using Symbolics\n\njulia> @variables x y;\n\njulia> @variables A[1:3, 1:3] B[1:3, 1:3];\n\njulia> x ~ y\nx ~ y\n\njulia> x - y ~ 0\nx - y ~ 0\n\njulia> A ~ B\n(broadcast(~, A, B))[1:3,1:3]\n\njulia> A .~ 3x\n(broadcast(~, A, 3x))[1:3,1:3]\n\n\n\n\n\n","category":"method"},{"location":"manual/variables/#A-note-about-functions-restricted-to-Numbers","page":"Variable and Equation Types","title":"A note about functions restricted to Numbers","text":"","category":"section"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Sym and Term objects are NOT subtypes of Number. Symbolics provides a simple wrapper type called Num which is a subtype of Real. Num wraps either a Sym or a Term or any other object, defines the same set of operations as symbolic expressions and forwards those to the values it wraps. You can use Symbolics.value function to unwrap a Num.","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"By default, the @variables macros return Num-wrapped objects to allow calling functions which are restricted to Number or Real.","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"using Symbolics\n@variables t x y z(t);\nSymbolics.operation(Symbolics.value(x + y))","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Symbolics.operation(Symbolics.value(z))","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Symbolics.arguments(Symbolics.value(x + y))","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Note that Julia converts irrationals — like π and ℯ — to Float64 whenever they are involved in arithmetic with other numbers, including integers. An expression like 2π will be converted to a float immediately, so an expression like 2π * x will leave the symbolic x multiplied by a Float64. It may be preferable to have a symbolic representation of π also, which can be achieved with Num(π). For generic programming, it may be helpful to simply redefine the variable π to be of the same type as some other argument, as in","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"function f(x)\n let π=oftype(x, π)\n 1 + (2//3 + 4π/5) * x\n end\nend\nf(t)","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"This will work for any floating-point input, as well as symbolic input.","category":"page"},{"location":"manual/variables/#Symbolic-Control-Flow","page":"Variable and Equation Types","title":"Symbolic Control Flow","text":"","category":"section"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Control flow can be expressed in Symbolics.jl in the following ways:","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"IfElse.ifelse(cond,x,y): this is a dispatch-able version of the ifelse function provided by IfElse.jl which allows for encoding conditionals in the symbolic branches.","category":"page"},{"location":"manual/variables/#Inspection-Functions","page":"Variable and Equation Types","title":"Inspection Functions","text":"","category":"section"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"SymbolicUtils.iscall\nSymbolicUtils.operation\nSymbolicUtils.arguments","category":"page"},{"location":"manual/sparsity_detection/#Structure-and-Sparsity-Detection","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"","category":"section"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"Using the tracing system provided by Symbolics.jl expressions, Symbolics.jl can automatically detect the sparsity patterns of Julia functions efficiently. This functionality is described in more detail in the paper:","category":"page"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"@article{gowda2019sparsity,\n title={Sparsity Programming: Automated Sparsity-Aware Optimizations in Differentiable Programming},\n author={Gowda, Shashi and Ma, Yingbo and Churavy, Valentin and Edelman, Alan and Rackauckas, Christopher},\n year={2019}\n}","category":"page"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"Please cite this work if the functionality is used.","category":"page"},{"location":"manual/sparsity_detection/#Sparsity-Detection","page":"Structure and Sparsity Detection","title":"Sparsity Detection","text":"","category":"section"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"Symbolics.jacobian_sparsity\nSymbolics.hessian_sparsity","category":"page"},{"location":"manual/sparsity_detection/#Symbolics.jacobian_sparsity","page":"Structure and Sparsity Detection","title":"Symbolics.jacobian_sparsity","text":"jacobian_sparsity(\n exprs::AbstractArray,\n vars::AbstractArray\n) -> SparseArrays.SparseMatrixCSC{Bool, Int64}\n\n\nReturn the sparsity pattern of the Jacobian of an array of expressions with respect to an array of variable expressions.\n\nArguments\n\nexprs: an array of symbolic expressions.\nvars: an array of symbolic variables.\n\nExamples\n\njulia> using Symbolics\n\njulia> vars = @variables x₁ x₂;\n\njulia> exprs = [2x₁, 3x₂, 4x₁ * x₂];\n\njulia> Symbolics.jacobian_sparsity(exprs, vars)\n3×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 4 stored entries:\n 1 ⋅\n ⋅ 1\n 1 1\n\n\n\n\n\njacobian_sparsity(\n f!::Function,\n output::AbstractArray,\n input::AbstractArray,\n args...;\n kwargs...\n) -> SparseArrays.SparseMatrixCSC{Bool, Int64}\n\n\nReturn the sparsity pattern of the Jacobian of the mutating function f!.\n\nArguments\n\nf!: an in-place function f!(output, input, args...; kwargs...).\noutput: output array.\ninput: input array.\n\nThe eltype of output and input can be either symbolic or primitive.\n\nExamples\n\njulia> using Symbolics\n\njulia> f!(y, x) = y .= [x[2], 2x[1], 3x[1] * x[2]];\n\njulia> output = Vector{Float64}(undef, 3);\n\njulia> input = Vector{Float64}(undef, 2);\n\njulia> Symbolics.jacobian_sparsity(f!, output, input)\n3×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 4 stored entries:\n ⋅ 1\n 1 ⋅\n 1 1\n\n\n\n\n\n","category":"function"},{"location":"manual/sparsity_detection/#Symbolics.hessian_sparsity","page":"Structure and Sparsity Detection","title":"Symbolics.hessian_sparsity","text":"hessian_sparsity(\n expr,\n vars::AbstractVector;\n full\n) -> Union{SparseArrays.SparseMatrixCSC{Bool, Int64}, SparseArrays.SparseMatrixCSC{Int64, Int64}}\n\n\nReturn the sparsity pattern of the Hessian of an expression with respect to an array of variable expressions.\n\nArguments\n\nexpr: a symbolic expression.\nvars: a vector of symbolic variables.\n\nExamples\n\njulia> using Symbolics\n\njulia> vars = @variables x₁ x₂;\n\njulia> expr = 3x₁^2 + 4x₁ * x₂;\n\njulia> Symbolics.hessian_sparsity(expr, vars)\n2×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 3 stored entries:\n 1 1\n 1 ⋅\n\n\n\n\n\nhessian_sparsity(\n f::Function,\n input::AbstractVector,\n args...;\n full,\n kwargs...\n) -> Union{SparseArrays.SparseMatrixCSC{Bool, Int64}, SparseArrays.SparseMatrixCSC{Int64, Int64}}\n\n\nReturn the sparsity pattern of the Hessian of the given function f.\n\nArguments\n\nf: an out-of-place function f(input, args...; kwargs...).\ninput: a vector of input values whose eltype can be either symbolic or primitive.\n\nExamples\n\njulia> using Symbolics\n\njulia> f(x) = 4x[1] * x[2] - 5x[2]^2;\n\njulia> input = Vector{Float64}(undef, 2);\n\njulia> Symbolics.hessian_sparsity(f, input)\n2×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 3 stored entries:\n ⋅ 1\n 1 1\n\n\n\n\n\n","category":"function"},{"location":"manual/sparsity_detection/#Structure-Detection","page":"Structure and Sparsity Detection","title":"Structure Detection","text":"","category":"section"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"Symbolics.islinear\nSymbolics.isaffine","category":"page"},{"location":"manual/sparsity_detection/#Symbolics.islinear","page":"Structure and Sparsity Detection","title":"Symbolics.islinear","text":"islinear(ex, u)\n\n\nCheck if an expression is linear with respect to a list of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/sparsity_detection/#Symbolics.isaffine","page":"Structure and Sparsity Detection","title":"Symbolics.isaffine","text":"isaffine(ex, u)\n\n\nCheck if an expression is affine with respect to a list of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/sparsity_detection/#ADTypes.jl-interface","page":"Structure and Sparsity Detection","title":"ADTypes.jl interface","text":"","category":"section"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"Symbolics.SymbolicsSparsityDetector","category":"page"},{"location":"manual/sparsity_detection/#Symbolics.SymbolicsSparsityDetector","page":"Structure and Sparsity Detection","title":"Symbolics.SymbolicsSparsityDetector","text":"SymbolicsSparsityDetector <: ADTypes.AbstractSparsityDetector\n\nSparsity detection algorithm based on the Symbolics.jl tracing system.\n\nThis type makes Symbolics.jl compatible with the ADTypes.jl sparsity detection framework. The following functions are implemented:\n\nADTypes.jacobian_sparsity based on Symbolics.jacobian_sparsity\nADTypes.hessian_sparsity based on Symbolics.hessian_sparsity\n\nReference\n\nSparsity Programming: Automated Sparsity-Aware Optimizations in Differentiable Programming, Gowda et al. (2019)\n\n\n\n\n\n","category":"type"},{"location":"tutorials/auto_parallel/#Automated-Sparse-Parallelism-of-Julia-Functions-via-Tracing","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"","category":"section"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"Because the Symbolics.jl expressions obey Julia semantics, one can directly transform existing Julia functions into Symbolics.jl symbolic representations of the function by simply inputting the symbolic values into the function and using what is returned. For example, let's take the following numerical PDE discretization:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"using Symbolics, LinearAlgebra, SparseArrays, Plots\n\n# Define the constants for the PDE\nconst α₂ = 1.0\nconst α₃ = 1.0\nconst β₁ = 1.0\nconst β₂ = 1.0\nconst β₃ = 1.0\nconst r₁ = 1.0\nconst r₂ = 1.0\nconst _DD = 100.0\nconst γ₁ = 0.1\nconst γ₂ = 0.1\nconst γ₃ = 0.1\nconst N = 32\nconst X = reshape([i for i in 1:N for j in 1:N], N, N)\nconst Y = reshape([j for i in 1:N for j in 1:N], N, N)\nconst α₁ = 1.0 .* (X .>= 4*N/5)\n\nconst Mx = Array(Tridiagonal([1.0 for i in 1:N-1], [-2.0 for i in 1:N], [1.0 for i in 1:N-1]))\nconst My = copy(Mx)\nMx[2, 1] = 2.0\nMx[end-1,end] = 2.0\nMy[1, 2] = 2.0\nMy[end,end-1] = 2.0\n\n# Define the discretized PDE as an ODE function\nfunction f(u, p, t)\n A = u[:,:,1]\n B = u[:,:,2]\n C = u[:,:,3]\n MyA = My*A\n AMx = A*Mx\n DA = @. _DD*(MyA + AMx)\n dA = @. DA + α₁ - β₁*A - r₁*A*B + r₂*C\n dB = @. α₂ - β₂*B - r₁*A*B + r₂*C\n dC = @. α₃ - β₃*C + r₁*A*B - r₂*C\n cat(dA, dB, dC, dims=3)\nend","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"We can build the Symbolics version of this model by tracing the model function:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"# Define the initial condition as normal arrays\n@variables u[1:N, 1:N, 1:3]\ndu = simplify.(f(collect(u), nothing, 0.0))\nvec(du)[1:10]","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"The output, here the in-place modified du, is a symbolic representation of each output of the function. We can then utilize this in the Symbolics functionality. For example, let's build a parallel version of f first:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"fastf = eval(Symbolics.build_function(du,u,\n parallel=Symbolics.MultithreadedForm())[2])","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"Now let's compute the sparse Jacobian function and compile a fast multithreaded version:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"jac = Symbolics.sparsejacobian(vec(du), vec(u))\nrow,col,val = findnz(jac)\nscatter(row,col,legend=false,ms=1,c=:black)","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"fjac = eval(Symbolics.build_function(jac,u,\n parallel=Symbolics.MultithreadedForm())[2])","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"It takes awhile for this to generate, but the results will be worth it! Now let's set up the parabolic PDE to be solved by DifferentialEquations.jl. We will set up the vanilla version and the sparse multithreaded version:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"using OrdinaryDiffEq\nu0 = zeros(N, N, 3)\nMyA = zeros(N, N);\nAMx = zeros(N, N);\nDA = zeros(N, N);\nprob = ODEProblem(f, u0, (0.0, 10.0))\nfastprob = ODEProblem(ODEFunction((du, u, p, t) -> fastf(du, u),\n jac = (du, u, p, t) -> fjac(du, u),\n jac_prototype = similar(jac, Float64)),\n u0, (0.0, 10.0))","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"Let's see the timing difference:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"using BenchmarkTools\n#@btime solve(prob, TRBDF2()); # 33.073 s (895404 allocations: 23.87 GiB)\n#warning the following solve takes a long time to compile, but afterwards is very fast.\n#@btime solve(fastprob, TRBDF2()); # 209.670 ms (8208 allocations: 109.25 MiB)","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"Boom, an automatic 157x acceleration that grows as the size of the problem increases!","category":"page"},{"location":"manual/io/#I/O,-Saving,-and-Latex","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"","category":"section"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"Note that Julia's standard I/O functionality can be used to save Symbolics expressions out to files. For example, here we will generate an in-place version of f and save the anonymous function to a .jl file:","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"using Symbolics\n@variables u[1:3]\nfunction f(u)\n [u[1]-u[3],u[1]^2-u[2],u[3]+u[2]]\nend\nex1, ex2 = build_function(f(u),u)\nwrite(\"function.jl\", string(ex2))","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"Now we can do something like:","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"g = include(\"function.jl\")","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"and that will load the function back in. Note that this can be done to save the transformation results of Symbolics.jl so that they can be stored and used in a precompiled Julia package.","category":"page"},{"location":"manual/io/#Latexification","page":"I/O, Saving, and Latex","title":"Latexification","text":"","category":"section"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"Symbolics.jl's expressions support Latexify.jl, and thus","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"using Latexify\nlatexify(ex)","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"will produce LaTeX output from Symbolics models and expressions. This works on basics like Term all the way to higher primitives like ODESystem and ReactionSystem.","category":"page"},{"location":"manual/solver/#Solver","page":"Solver","title":"Solver","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"The main symbolic solver for Symbolics.jl is symbolic_solve. Symbolic solving means that it only uses symbolic (algebraic) methods and outputs exact solutions.","category":"page"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"Symbolics.symbolic_solve","category":"page"},{"location":"manual/solver/#Symbolics.symbolic_solve","page":"Solver","title":"Symbolics.symbolic_solve","text":"symbolic_solve(expr, x; dropmultiplicity=true, warns=true)\n\nsymbolic_solve is a function which attempts to solve input equations/expressions symbolically using various methods.\n\nArguments\n\nexpr: Could be a single univar expression in the form of a poly or multiple univar expressions or multiple multivar polys or a transcendental nonlinear function.\nx: Could be a single variable or an array of variables which should be solved\ndropmultiplicity (optional): Should the output be printed n times where n is the number of occurrence of the root? Say we have (x+1)^2, we then have 2 roots x = -1, by default the output is [-1], If dropmultiplicity is inputted as false, then the output is [-1, -1].\nwarns (optional): When invalid expressions or cases are inputted, should the solver warn you of such cases before returning nothing? if this is set to false, the solver returns nothing. By default, warns are set to true.\n\nSupported input\n\nThe base solver (symbolic_solve) has multiple solvers which chooses from depending on the the type of input (multiple/uni var and multiple/single expression) only after ensuring that the input is valid.\n\nThe expressions inputted can contain parameters, which are assumed to be transcendental. A parameter \"a\" is transcendental if there exists no polynomial P with rational coefficients such that P(a) = 0. Check the examples section.\n\nCurrently, symbolic_solve supports\n\nLinear and polynomial equations (with parameters)\nSystems of linear and polynomials equations (without extra parameters, for now)\nEquations with transcendental functions (with parameters)\n\nExamples\n\nsolve_univar (uses factoring and analytic solutions up to degree 4)\n\nnote: Note\nThe package Nemo is needed in order to use solve_univar as well as solve_multipoly, so executing using Nemo as you will see in the following examples is necessary; otherwise, the function will throw an error.\n\njulia> using Symbolics, Nemo;\n\njulia> @variables x a b;\n\njulia> expr = expand((x + b)*(x^2 + 2x + 1)*(x^2 - a))\n-a*b - a*x - 2a*b*x - 2a*(x^2) + b*(x^2) + x^3 - a*b*(x^2) - a*(x^3) + 2b*(x^3) + 2(x^4) + b*(x^4) + x^5\n\njulia> symbolic_solve(expr, x)\n4-element Vector{Any}:\n -1\n -b\n (1//2)*√(4a)\n (-1//2)*√(4a)\n\njulia> symbolic_solve(expr, x, dropmultiplicity=false)\n5-element Vector{Any}:\n -1\n -1\n -b\n (1//2)*√(4a)\n (-1//2)*√(4a)\n\njulia> symbolic_solve(x^2 + a*x + 6, x)\n2-element Vector{SymbolicUtils.BasicSymbolic{Real}}:\n (1//2)*(-a + √(-24 + a^2))\n (1//2)*(-a - √(-24 + a^2))\n\njulia> symbolic_solve(x^7 - 1, x)\n2-element Vector{Any}:\n roots_of((1//1) + x + x^2 + x^3 + x^4 + x^5 + x^6, x)\n 1\n\nsolve_multivar (uses Groebner basis and solve_univar to find roots)\n\nnote: Note\nSimilar to solve_univar, Groebner is needed for solve_multivar or to be fully functional.\n\njulia> using Groebner\n\njulia> @variables x y z\n3-element Vector{Num}:\n x\n y\n z\n\njulia> eqs = [x+y^2+z, z*x*y, z+3x+y]\n3-element Vector{Num}:\n x + z + y^2\n x*y*z\n 3x + y + z\n\njulia> symbolic_solve(eqs, [x,y,z])\n3-element Vector{Any}:\n Dict{Num, Any}(z => 0, y => 1//3, x => -1//9)\n Dict{Num, Any}(z => 0, y => 0, x => 0)\n Dict{Num, Any}(z => -1, y => 1, x => 0)\n\nnote: Note\nIf Nemo or Groebner are not imported when needed, the solver throws an error.\n\njulia> using Symbolics\n\njulia> @variables x y z;\n\njulia> symbolic_solve(x+1, x)\nERROR: \"Nemo is required. Execute `using Nemo` to enable this functionality.\"\n\njulia> symbolic_solve([x+1, y], [x, y])\nERROR: \"Groebner bases engine is required. Execute `using Groebner` to enable this functionality.\"\n\nsolve_multipoly (uses GCD between the input polys)\n\njulia> symbolic_solve([x-1, x^3 - 1, x^2 - 1, (x-1)^20], x)\n1-element Vector{BigInt}:\n 1\n\nia_solve (solving by isolation and attraction)\n\njulia> symbolic_solve(2^(x+1) + 5^(x+3), x)\n1-element Vector{SymbolicUtils.BasicSymbolic{Real}}:\n (-slog(2) - log(complex(-1)) + 3slog(5)) / (slog(2) - slog(5))\n\njulia> symbolic_solve(log(x+1)+log(x-1), x)\n2-element Vector{SymbolicUtils.BasicSymbolic{BigFloat}}:\n (1//2)*√(8.0)\n (-1//2)*√(8.0)\n\njulia> symbolic_solve(a*x^b + c, x)\n((-c)^(1 / b)) / (a^(1 / b))\n\nEvaluating output (converting to floats)\n\nIf you want to evaluate the exact expressions found by symbolic_solve, you can do the following:\n\njulia> roots = symbolic_solve(2^(x+1) + 5^(x+3), x)\n1-element Vector{SymbolicUtils.BasicSymbolic{Real}}:\n (-slog(2) - log(complex(-1)) + 3slog(5)) / (slog(2) - slog(5))\n\njulia> Symbolics.symbolic_to_float.(roots)\n1-element Vector{Complex{BigFloat}}:\n -4.512941594732059759689023145584186058252768936052415430071569066192919491762214 + 3.428598090438030380369414618548038962770087500755160535832807433942464545729382im\n\n\n\n\n\n","category":"function"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"One other symbolic solver is symbolic_linear_solve which is limited compared to symbolic_solve as it only solves linear equations.","category":"page"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"Symbolics.symbolic_linear_solve","category":"page"},{"location":"manual/solver/#Symbolics.symbolic_linear_solve","page":"Solver","title":"Symbolics.symbolic_linear_solve","text":"symbolic_linear_solve(eq, var; simplify, check) -> Any\n\n\nSolve equation(s) eqs for a set of variables vars.\n\nAssumes length(eqs) == length(vars)\n\nCurrently only works if all equations are linear. check if the expr is linear w.r.t vars.\n\nExamples\n\njulia> @variables x y\n2-element Vector{Num}:\n x\n y\n\njulia> Symbolics.symbolic_linear_solve(x + y ~ 0, x)\n-y\n\njulia> Symbolics.symbolic_linear_solve([x + y ~ 0, x - y ~ 2], [x, y])\n2-element Vector{Float64}:\n 1.0\n -1.0\n\n\n\n\n\n","category":"function"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"symbolic_solve only supports symbolic, i.e. non-floating point computations, and thus prefers equations where the coefficients are integer, rational, or symbolic. Floating point coefficients are transformed into rational values and BigInt values are used internally with a potential performance loss, and thus it is recommended that this functionality is only used with floating point values if necessary. In contrast, symbolic_linear_solve directly handles floating point values using standard factorizations.","category":"page"},{"location":"manual/solver/#More-technical-details-and-examples","page":"Solver","title":"More technical details and examples","text":"","category":"section"},{"location":"manual/solver/#Technical-details","page":"Solver","title":"Technical details","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"The symbolic_solve function uses 4 hidden solvers in order to solve the user's input. Its base, solve_univar, uses analytic solutions up to polynomials of degree 4 and factoring as its method for solving univariate polynomials. The function's solve_multipoly uses GCD on the input polynomials then throws passes the result to solve_univar. The function's solve_multivar uses Groebner basis and a separating form in order to create linear equations in the input variables and a single high degree equation in the separating variable [1]. Each equation resulting from the basis is then passed to solve_univar. We can see that essentially, solve_univar is the building block of symbolic_solve. If the input is not a valid polynomial and can not be solved by the algorithm above, symbolic_solve passes it to ia_solve, which attempts solving by attraction and isolation [2]. This only works when the input is a single expression and the user wants the answer in terms of a single variable. Say log(x) - a == 0 gives us [e^a].","category":"page"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"Symbolics.solve_univar\nSymbolics.solve_multivar\nSymbolics.ia_solve\nSymbolics.ia_conditions!\nSymbolics.is_periodic\nSymbolics.fundamental_period","category":"page"},{"location":"manual/solver/#Symbolics.solve_univar","page":"Solver","title":"Symbolics.solve_univar","text":"solve_univar(expression, x; dropmultiplicity=true)\n\nThis solver uses analytic solutions up to degree 4 to solve univariate polynomials. It first handles the special case of the expression being of operation ^. E.g. math (x+2)^{20}. We solve this by removing the int 20, then solving the poly math x+2 on its own. If the parameter mult of the solver is set to true, we then repeat the found roots of math x+2 twenty times before returning the results to the user.\n\nStep 2 is filtering the expression after handling this special case, and then factoring it using factor_use_nemo. We then solve all the factors outputted using the analytic methods implemented in the function get_roots and its children.\n\nArguments\n\nexpr: Single symbolics Num or SymbolicUtils.BasicSymbolic expression. This is equated to 0 and then solved. E.g. expr = x+2, we solve x+2 = 0\nx: Single symbolics variable\ndropmultiplicity (optional): Print repeated roots or not?\nstrict (optional): Bool that enables/disables strict assert if input expression is a univariate polynomial or not. If strict=true and expression is not a polynomial, solve_univar throws an assertion error.\n\nExamples\n\n\n\n\n\n","category":"function"},{"location":"manual/solver/#Symbolics.ia_solve","page":"Solver","title":"Symbolics.ia_solve","text":"ia_solve(lhs, var; kwargs...)\n\nThis function attempts to solve transcendental functions by first checking the \"smart\" number of occurrences in the input LHS. By smart here we mean that polynomials are counted as 1 occurrence. for example x^2 + 2x is 1 occurrence of x. So we abstract all occurrences of x's as polynomials. Say: log(x+1) + x^2 is seen as log(f(x)) + g(x) so there are 2 occurrences of x. If there is only 1 occurrence of x in an input expression, isolate is called.\n\nIsolate reverses all operations applied on the occurrence of x until we have f(x) = some constant then we can solve this using our polynomial solvers.\n\nIf more than 1 occurrence of x is found, ia_solve attempts to attract the occurrences of x in order to reduce these occurrences to 1. For example, log(x+1) + log(x-1) can be converted to log(x^2 - 1) which now could be isolated using Isolate.\n\nattract(lhs, var) currently uses 4 techniques for attraction.\n\nLog addition: log(f(x)) + log(g(x)) => log(h(x))\nExponential simplification: a*b^(f(x)) + c*d^(g(x)) => f(x) * log(b) - g(x) * log(d) + log(-a/c). And now this is actually 1 occurrence of x since f(x) and g(x) are just multiplied by constants not wrapped in some operation.\nTrig simplification: this bruteforces multiple trig identities and doesn't detect them before hand.\nPolynomialization: as a last resort, attract attempts to polynomialize the expression. Say sin(x+2)^2 + sin(x+2) + 10 is converted to X^2 + X + 10, we then solve this using our polynomial solver, and afterwards, isolate sin(x+2) = the roots found by solve for X^2 + X + 10\n\nAfter attraction, we check the number of occurrences again, and if its 1, we isolate, if not, we throw an error to tell the user that this is currently unsolvable by our covered techniques.\n\nArguments\n\nlhs: a Num/SymbolicUtils.BasicSymbolic\nvar: variable to solve for.\n\nKeyword arguments\n\nwarns = true: Whether to emit warnings for unsolvable expressions.\ncomplex_roots = true: Whether to consider complex roots of x ^ n ~ y, where n is an integer.\nperiodic_roots = true: If true, isolate f(x) ~ y as x ~ finv(y) + n * period where is_periodic(f) == true, finv = left_inverse(f) and period = fundamental_period(f). n is a new anonymous symbolic variable.\n\nExamples\n\njulia> solve(a*x^b + c, x)\n((-c)^(1 / b)) / (a^(1 / b))\n\njulia> solve(2^(x+1) + 5^(x+3), x)\n1-element Vector{SymbolicUtils.BasicSymbolic{Real}}:\n (-log(2) + 3log(5) - log(complex(-1))) / (log(2) - log(5))\n\njulia> solve(log(x+1)+log(x-1), x)\n2-element Vector{SymbolicUtils.BasicSymbolic{Real}}:\n (1//2)*RootFinding.ssqrt(8.0)\n (-1//2)*RootFinding.ssqrt(8.0)\n\njulia> expr = sin(x+2)^2 + sin(x+2) + 10\n10 + sin(2 + x) + sin(2 + x)^2\n\njulia> RootFinding.ia_solve(expr, x)\n[ Info: var\"##230\" ϵ Ζ: e.g. 0, 1, 2...\n[ Info: var\"##234\" ϵ Ζ: e.g. 0, 1, 2...\n2-element Vector{SymbolicUtils.BasicSymbolic{Real}}:\n -2 + π*2var\"##230\" + asin((1//2)*(-1 + RootFinding.ssqrt(-39)))\n -2 + π*2var\"##234\" + asin((1//2)*(-1 - RootFinding.ssqrt(-39)))\n\nAll transcendental functions for which left_inverse is defined are supported. To enable ia_solve to handle custom transcendental functions, define an inverse or left inverse. If the function is periodic, is_periodic and fundamental_period must be defined. If the function imposes certain conditions on its input or output (for example, log requires that its input be positive) define ia_conditions!.\n\nSee also: left_inverse, inverse, is_periodic, fundamental_period, ia_conditions!.\n\nReferences\n\n[1]: R. W. Hamming, Coding and Information Theory, ScienceDirect, 1980.\n\n\n\n\n\n","category":"function"},{"location":"manual/solver/#Symbolics.ia_conditions!","page":"Solver","title":"Symbolics.ia_conditions!","text":"ia_conditions!(f, lhs, rhs::Vector{Any}, conditions::Vector{Tuple})\n\nIf f is a left-invertible function, lhs and rhs[i] are univariate functions and f(lhs) ~ rhs[i] for all i in eachindex(rhss), push to conditions all the relevant conditions on lhs or rhs[i]. Each condition is of the form (sym, op) where sym is an expression involving lhs and/or rhs[i] and op is a binary relational operator. The condition op(sym, 0) is then required to be true for the equation f(lhs) ~ rhs[i] to be valid.\n\nFor example, if f = log, lhs = x and rhss = [y, z] then the condition x > 0 must be true. Thus, (lhs, >) is pushed to conditions. Similarly, if f = sqrt, rhs[i] >= 0 must be true for all i, and so (y, >=) and (z, >=) will be appended to conditions. \n\n\n\n\n\n","category":"function"},{"location":"manual/solver/#Symbolics.is_periodic","page":"Solver","title":"Symbolics.is_periodic","text":"is_periodic(f)\n\nReturn true if f is a single-input single-output periodic function. Return false by default. If is_periodic(f) == true, then fundamental_period(f) must also be defined.\n\nSee also: fundamental_period\n\n\n\n\n\n","category":"function"},{"location":"manual/solver/#Symbolics.fundamental_period","page":"Solver","title":"Symbolics.fundamental_period","text":"fundamental_period(f)\n\nReturn the fundamental period of periodic function f. Must only be called if is_periodic(f) == true.\n\nsee also: is_periodic\n\n\n\n\n\n","category":"function"},{"location":"manual/solver/#Nice-examples","page":"Solver","title":"Nice examples","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"using Symbolics, Nemo;\n@variables x;\nSymbolics.symbolic_solve(9^x + 3^x ~ 8, x)","category":"page"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"@variables x y z;\nSymbolics.symbolic_linear_solve(2//1*x + y - 2//1*z ~ 9//1*x, 1//1*x)","category":"page"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"using Groebner;\n@variables x y z;\n\neqs = [x^2 + y + z - 1, x + y^2 + z - 1, x + y + z^2 - 1]\nSymbolics.symbolic_solve(eqs, [x,y,z])","category":"page"},{"location":"manual/solver/#Feature-completeness","page":"Solver","title":"Feature completeness","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"[x] Linear and polynomial equations\n[x] Systems of linear and polynomial equations\n[x] Some transcendental functions\n[x] Systems of linear equations with parameters (via symbolic_linear_solve)\n[ ] Equations with radicals\n[x] Systems of polynomial equations with parameters and positive dimensional systems\n[ ] Inequalities","category":"page"},{"location":"manual/solver/#Expressions-we-can-not-solve-(but-aim-to)","page":"Solver","title":"Expressions we can not solve (but aim to)","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"# Mathematica\n\nIn[1]:= Reduce[x^2 - x - 6 > 0, x]\nOut[1]= x < -2 || x > 3\n\nIn[2]:= Reduce[x+a > 0, x]\nOut[2]= a \\[Element] Reals && x > -a\n\nIn[3]:= Solve[x^(x) + 3 == 0, x]\nOut[3]= {{x -> (I \\[Pi] + Log[3])/ProductLog[I \\[Pi] + Log[3]]}}","category":"page"},{"location":"manual/solver/#References","page":"Solver","title":"References","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"[1]: Rouillier, F. Solving Zero-Dimensional Systems Through the Rational Univariate Representation. AAECC 9, 433–461 (1999).","category":"page"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"[2]: R. W. Hamming, Coding and Information Theory, ScienceDirect, 1980.","category":"page"},{"location":"tutorials/converting_to_C/#Automatic-Conversion-of-Julia-Code-to-C-Functions","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"","category":"section"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"Since Symbolics.jl can trace Julia code into Symbolics IR that can be built and compiled via build_function to C, this gives us a nifty way to automatically generate C functions from Julia code! To see this in action, let's start with the Lotka-Volterra equations:","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"using Symbolics\nfunction lotka_volterra!(du, u, p, t)\n x, y = u\n α, β, δ, γ = p\n du[1] = dx = α*x - β*x*y\n du[2] = dy = -δ*y + γ*x*y\nend","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"Now we trace this into Symbolics:","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"@variables t du[1:2] u[1:2] p[1:4]\ndu = collect(du)\nlotka_volterra!(du, u, p, t)\ndu","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"and then we build the function:","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"build_function(du, u, p, t, target=Symbolics.CTarget())","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"If we want to compile this, we do expression=Val{false}:","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"f = build_function(du, u, p, t, target=Symbolics.CTarget(), expression=Val{false})","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"now we check it computes the same thing:","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"du = rand(2); du2 = rand(2)\nu = rand(2)\np = rand(4)\nt = rand()\nf(du, u, p, t)\nlotka_volterra!(du2, u, p, t)\ndu == du2 # true!","category":"page"},{"location":"manual/build_function/#Function-Building-and-Compilation-(build_function)","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"","category":"section"},{"location":"manual/build_function/","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"At any time, callable functions can be generated from Symbolics IR by using Symbolics.toexpr. This performs some cleaning to return an expression without extraneous pieces that commonly matches expressions one would write in functions like those for differential equation solvers and optimization libraries. These functions can be automatically parallelized and specialize on Julia types like static arrays and sparse matrices.","category":"page"},{"location":"manual/build_function/","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"The core compilation process of Symbolics IR is build_function. build_function takes an operation or an AbstractArray of operations and generates a compilable version of the model for numerical solvers. The form of this output is dependent on the target. By default, the target outputs Julia code, but other formats, such as C, Stan, and MATLAB are available. These can be generated as expressions which can then be evaluated into a callable function, or the compilers for the respective targets can be invoked to directly give back the function handle.","category":"page"},{"location":"manual/build_function/#build_function","page":"Function Building and Compilation (build_function)","title":"build_function","text":"","category":"section"},{"location":"manual/build_function/","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"build_function","category":"page"},{"location":"manual/build_function/#Symbolics.build_function","page":"Function Building and Compilation (build_function)","title":"Symbolics.build_function","text":"build_function(ex, args...;\n expression = Val{true},\n target = JuliaTarget(),\n parallel=nothing,\n kwargs...)\n\nGenerates a numerically-usable function from a Symbolics Num.\n\nArguments:\n\nex: The Num to compile\nargs: The arguments of the function\nexpression: Whether to generate code or whether to generate the compiled form. By default, expression = Val{true}, which means that the code for the function is returned. If Val{false}, then the returned value is compiled.\n\nKeyword Arguments:\n\ntarget: The output target of the compilation process. Possible options are:\nJuliaTarget: Generates a Julia function\nCTarget: Generates a C function\nStanTarget: Generates a function for compiling with the Stan probabilistic programming language\nMATLABTarget: Generates an anonymous function for use in MATLAB and Octave environments\nparallel: The kind of parallelism to use in the generated function. Defaults to SerialForm(), i.e. no parallelism, if ex is a single expression or an array containing <= 1500 non-zero expressions. If ex is an array of > 1500 non-zero expressions, then ShardedForm(80, 4) is used. See below for more on ShardedForm. Note that the parallel forms are not exported and thus need to be chosen like Symbolics.SerialForm(). The choices are:\nSerialForm(): Serial execution.\nShardedForm(cutoff, ncalls): splits the output function into sub-functions which contain at most cutoff number of output rhss. These sub-functions are called by the top-level function that buildfunction returns. This helps in reducing the compile time of the generated function.\nMultithreadedForm(): Multithreaded execution with a static split, evenly splitting the number of expressions per thread.\nfname: Used by some targets for the name of the function in the target space.\n\nNote that not all build targets support the full compilation interface. Check the individual target documentation for details.\n\n\n\n\n\n","category":"function"},{"location":"manual/build_function/#Target-Specific-Definitions","page":"Function Building and Compilation (build_function)","title":"Target-Specific Definitions","text":"","category":"section"},{"location":"manual/build_function/","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"Symbolics._build_function(target::Symbolics.JuliaTarget,rhss::AbstractArray,args...;kwargs...)\nSymbolics._build_function(target::Symbolics.CTarget,eqs::Array{<:Equation},args...;kwargs...)\nSymbolics._build_function(target::Symbolics.StanTarget,eqs::Array{<:Equation}, vs, ps, iv;kwargs...)\nSymbolics._build_function(target::Symbolics.MATLABTarget,eqs::Array{<:Equation},args...;kwargs...)","category":"page"},{"location":"manual/build_function/#Symbolics._build_function-Tuple{Symbolics.JuliaTarget, AbstractArray, Vararg{Any}}","page":"Function Building and Compilation (build_function)","title":"Symbolics._build_function","text":"_build_function(target::JuliaTarget, rhss::AbstractArray, args...;\n conv=toexpr,\n expression = Val{true},\n expression_module = @__MODULE__(),\n checkbounds = false,\n postprocess_fbody=ex -> ex,\n linenumbers = false,\n outputidxs=nothing,\n skipzeros = false,\n force_SA = false,\n wrap_code = (nothing, nothing),\n fillzeros = skipzeros && !(rhss isa SparseMatrixCSC),\n states = LazyState(),\n iip_config = (true, true),\n parallel=nothing, cse = false, kwargs...)\n\nBuild function target: JuliaTarget\n\n_build_function(target::JuliaTarget, rhss, args...;\n conv = toexpr,\n expression = Val{true},\n checkbounds = false,\n linenumbers = false,\n headerfun = addheader, outputidxs=nothing,\n convert_oop = true, force_SA = false,\n skipzeros = outputidxs===nothing,\n fillzeros = skipzeros && !(typeof(rhss)<:SparseMatrixCSC),\n parallel=SerialForm(), kwargs...)\n\nGenerates a Julia function which can then be utilized for further evaluations. If expression=Val{false}, the return is a Julia function which utilizes RuntimeGeneratedFunctions.jl to be free of world-age issues.\n\nIf the rhss is a scalar, the generated function is a function with a scalar output. Otherwise, if it's an AbstractArray, the output is two functions, one for out-of-place AbstractArray output and a second which is a mutating function. The outputted functions match the given argument order, i.e., f(u,p,args...) for the out-of-place and scalar functions and f!(du,u,p,args..) for the in-place version.\n\nSpecial Keyword Arguments:\n\nparallel: The kind of parallelism to use in the generated function. Defaults to SerialForm(), i.e. no parallelism. Note that the parallel forms are not exported and thus need to be chosen like Symbolics.SerialForm(). The choices are:\nSerialForm(): Serial execution.\nShardedForm(cutoff, ncalls): splits the output function into sub-functions which contain at most cutoff number of output rhss. These sub-functions are called by the top-level function that buildfunction returns.\nMultithreadedForm(): Multithreaded execution with a static split, evenly splitting the number of expressions per thread.\nconv: The conversion function of symbolic types to Expr. By default, this uses the toexpr function.\ncheckbounds: For whether to enable bounds checking inside the generated function. Defaults to false, meaning that @inbounds is applied.\nlinenumbers: Determines whether the generated function expression retains the line numbers. Defaults to true.\nconvert_oop: Determines whether the OOP version should try to convert the output to match the type of the first input. This is useful for cases like LabelledArrays or other array types that carry extra information. Defaults to true.\nforce_SA: Forces the output of the OOP version to be a StaticArray. Defaults to false, and outputs a static array when the first argument is a static array.\nskipzeros: Whether to skip filling zeros in the in-place version if the filling function is 0.\nfillzeros: Whether to perform fill(out,0) before the calculations to ensure safety with skipzeros.\n\n\n\n\n\n","category":"method"},{"location":"manual/build_function/#Symbolics._build_function-Tuple{Symbolics.CTarget, Array{<:Equation}, Vararg{Any}}","page":"Function Building and Compilation (build_function)","title":"Symbolics._build_function","text":"Build function target: CTarget\n\n_build_function(target::CTarget, eqs::Array{<:Equation}, args...;\n conv = toexpr, expression = Val{true},\n fname = :diffeqf,\n lhsname=:du,rhsnames=[Symbol(\"RHS$i\") for i in 1:length(args)],\n libpath=tempname(), compiler=:gcc)\n\nThis builds an in-place C function. Only works on arrays of equations. If expression == Val{false}, then this builds a function in C, compiles it, and returns a lambda to that compiled function. These special keyword arguments control the compilation:\n\nlibpath: the path to store the binary. Defaults to a temporary path.\ncompiler: which C compiler to use. Defaults to :gcc, which is currently the only available option.\n\n\n\n\n\n","category":"method"},{"location":"manual/build_function/#Symbolics._build_function-Tuple{Symbolics.StanTarget, Array{<:Equation}, Any, Any, Any}","page":"Function Building and Compilation (build_function)","title":"Symbolics._build_function","text":"Build function target: StanTarget\n\n_build_function(target::StanTarget, eqs::Array{<:Equation}, vs, ps, iv;\n conv = toexpr, expression = Val{true},\n fname = :diffeqf, lhsname=:internal_var___du,\n rhsnames=[:internal_var___u,:internal_var___p,:internal_var___t])\n\nThis builds an in-place Stan function compatible with the Stan differential equation solvers. Unlike other build targets, this one requires (vs, ps, iv) as the function arguments. Only allowed on arrays of equations.\n\n\n\n\n\n","category":"method"},{"location":"manual/build_function/#Symbolics._build_function-Tuple{Symbolics.MATLABTarget, Array{<:Equation}, Vararg{Any}}","page":"Function Building and Compilation (build_function)","title":"Symbolics._build_function","text":"Build function target: MATLABTarget\n\n_build_function(target::MATLABTarget, eqs::Array{<:Equation}, args...;\n conv = toexpr, expression = Val{true},\n lhsname=:internal_var___du,\n rhsnames=[:internal_var___u,:internal_var___p,:internal_var___t])\n\nThis builds an out of place anonymous function @(t,rhsnames[1]) to be used in MATLAB. Compatible with the MATLAB differential equation solvers. Only allowed on expressions, and arrays of expressions.\n\n\n\n\n\n","category":"method"},{"location":"manual/build_function/#Limitations","page":"Function Building and Compilation (build_function)","title":"Limitations","text":"","category":"section"},{"location":"manual/build_function/","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"build_function ","category":"page"},{"location":"manual/arrays/#symbolic_arrays","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"","category":"section"},{"location":"manual/arrays/#Symbolic-Arrays-vs-Arrays-of-Symbolic-Expressions","page":"Symbolic Arrays","title":"Symbolic Arrays vs Arrays of Symbolic Expressions","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Symbolics.jl contains two forms for handling symbolic arrays:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Arrays of symbolic expressions: these are Julia arrays with Symbolics.jl objects in them.\nSymbolic Arrays: these are symbolic (O(1)) representations of arrays.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Arrays of symbolic expressions are simply Symbolics.jl objects put into Julia arrays. For example:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"using Symbolics\n@variables x y\nu = [x,y]","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"is a vector of two symbolic variables. As shorthand,","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"u2 = Symbolics.variables(:x, 1:3, 3:6)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"creates a Julia matrix of symbolic variables. Indexing u or u2 gives symbolic values which act as a normal scalar symbolic value. This form these uses Julia's array functionality and performs symbolic operations on the scalar values.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"On the otherhand, Julia's symbolic array form is an O(1) representation of the whole array.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"@variables A[1:5, 1:3]","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"When using this form, A[1,1] is not a symbolic variable but a symbolic expression for indexing the variable A. This representation holds linear algebra expressions in a non-expanded form. For example:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"@variables B[1:3, 1:3]\nA * B","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"in comparison to:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"a = Symbolics.variables(:a, 1:5, 1:3)\nb = Symbolics.variables(:b, 1:3, 1:3)\na * b","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"This makes the symbolic array form much more efficient, but requires that the expressions uses things with registered symbolic array functions which currently has much lower coverage. Also, there are many fallbacks for which arrays of symbolics which makes this approach more accessible but with larger expressions.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"We recommend defaulting to arrays of symbolics unless you need the expression symplifications of the symbolic array approach.","category":"page"},{"location":"manual/arrays/#Using-Symbolic-Arrays","page":"Symbolic Arrays","title":"Using Symbolic Arrays","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Symbolic array-valued expressions (symbolic arrays) are supported by Symbolics. Symbolic array expressions propagate useful metadata that depends on input arrays: array dimension, element type and shape.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"You can create a symbolic array variable with the following syntax:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"using Symbolics\n@variables A[1:5, 1:3] b[1:3]","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Here, A is a symbolic matrix of size (5, 3) and b is a symbolic vector of length 3.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"size(A)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"size(b)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"ndims(A)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"ndims(b)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"eltype(A)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"eltype(b)","category":"page"},{"location":"manual/arrays/#Array-operations","page":"Symbolic Arrays","title":"Array operations","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Operations on symbolic arrays return symbolic array expressions:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"c = A * b","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"size(c)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"eltype(c)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Adjoints, matrix-matrix, and matrix-vector multiplications are supported. Dot product returns a scalar-valued expression:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"b'b","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"size(b'b)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Outer product returns a matrix:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"b * b'","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"size(b*b')","category":"page"},{"location":"manual/arrays/#Broadcast,-map-and-reduce","page":"Symbolic Arrays","title":"Broadcast, map and reduce","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"A .* b'","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"map(asin, (A*b))","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"#sum(A) #latexify not working","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"typeof(sum(A))","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"typeof(sum(A, dims=2))","category":"page"},{"location":"manual/arrays/#Indexing-and-delayed-computation","page":"Symbolic Arrays","title":"Indexing and delayed computation","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Indexing array expressions is fairly flexible in Symbolics. Let's go through all the possible ways to index arrays.","category":"page"},{"location":"manual/arrays/#Scalar-indexing-and-scalarization","page":"Symbolic Arrays","title":"Scalar indexing and scalarization","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"AAt = A*A'","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"AAt[2,3]","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Here we indexed for the element (2,3), but we got back a symbolic indexing expression. You may want to force the element to be computed in terms of the elements of A. This can be done, using the scalarize function.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Symbolics.scalarize(AAt[2,3])","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"@syms i::Int j::Int\nSymbolics.scalarize(AAt[i,j])","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"In general, any scalar expression which is derived from array expressions can be scalarized.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"#sum(A[:,1]) + sum(A[2,:])#latexify not working","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Symbolics.scalarize(sum(A[:,1]) + sum(A[2,:]))","category":"page"},{"location":"manual/limits/#Symbolic-Limits","page":"Symbolic Limits","title":"Symbolic Limits","text":"","category":"section"},{"location":"manual/limits/","page":"Symbolic Limits","title":"Symbolic Limits","text":"Experimental symbolic limit support is provided by the limit function, documented below. See SymbolicLimits.jl for more information and implementation details.","category":"page"},{"location":"manual/limits/","page":"Symbolic Limits","title":"Symbolic Limits","text":"limit","category":"page"},{"location":"manual/limits/#Symbolics.limit","page":"Symbolic Limits","title":"Symbolics.limit","text":"limit(expr, var, h[, side::Symbol])\n\nCompute the limit of expr as var approaches h.\n\nside indicates the direction from which var approaches h. It may be one of :left, :right, or :both. If side is :both and the two sides do not align, an error is thrown. Side defaults to :both for finite h, :left for h = Inf, and :right for h = -Inf.\n\nexpr must be composed of log, exp, constants, and the rational operators +, -, *, and /. This limitation may eventually be relaxed.\n\nwarning: Warning\nBecause symbolic limit computation is undecidable, this function necessarily employs heuristics and may occasionally return wrong answers. Nevertheless, please report wrong answers as issues as we aim to have heuristics that produce correct answers in all practical cases.\n\n\n\n\n\n","category":"function"},{"location":"manual/functions/#function_registration","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Function registration is the ability to define new nodes in the symbolic graph. This is useful because symbolic computing is declarative, i.e. symbolic computations express what should be computed, not how it should be computed. However, at some level someone must describe how a given operation is computed. These are the primitive functions, and a symbolic expression is made up of primitive functions.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Symbolics.jl comes pre-registered with a large set of standard mathematical functions, like * and sin to special functions like erf, and even deeper operations like DataInterpolations.jl's AbstractInterpolation. However, in many cases you may need to add your own function, i.e. you may want to give an imperative code and use this to define a new symbolic code. Symbolics.jl calls the declaration of new declarative primitives from an imperative function definition registration. This page describes both the registration process and its companion process, tracing, for interacting with code written in Julia.","category":"page"},{"location":"manual/functions/#Direct-Tracing","page":"Function Registration and Tracing","title":"Direct Tracing","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Because Symbolics expressions respect Julia semantics, one way to generate symbolic expressions is to simply place Symbolics variables as inputs into existing Julia code. For example, the following uses the standard Julia function for the Lorenz equations to generate the symbolic expression for the Lorenz equations:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"using Symbolics\nfunction lorenz(du,u,p,t)\n du[1] = 10.0(u[2]-u[1])\n du[2] = u[1]*(28.0-u[3]) - u[2]\n du[3] = u[1]*u[2] - (8/3)*u[3]\nend\n@variables t p[1:3] u(t)[1:3]\ndu = Array{Any}(undef, 3)\nlorenz(du,u,p,t)\ndu","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Or similarly:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"@variables t x(t) y(t) z(t) dx(t) dy(t) dz(t) σ ρ β\ndu = [dx,dy,dz]\nu = [x,y,z]\np = [σ,ρ,β]\nlorenz(du,u,p,t)\ndu","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Note that what has been done here is that the imperative Julia code for the function lorenz has been transformed into a declarative symbolic graph. Importantly, the code of lorenz is transformed into an expression consisting only of primitive registered functions, things like * and -, which come pre-registered with Symbolics.jl This then allows for symbolic manipulation of the expressions, allowing things like simplification and operation reordering done on its generated expressions. ","category":"page"},{"location":"manual/functions/#Utility-and-Scope-of-Tracing","page":"Function Registration and Tracing","title":"Utility and Scope of Tracing","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"This notably describes one limitation of tracing: tracing only works if the expression being traced is composed of already registered functions. If unregistered functions, such as calls to C code, are used, then the tracing process will error.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"However, we note that symbolic tracing by definition does not guarantee that the exact choices. The symbolic expressions may re-distribute the arithmetic, simplify out expressions, or do other modifications. Thus if this function is function is sensitive to numerical details in its calculation, one would not want to trace the function and thus would instead register it as a new primitive function.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"For the symbolic system to be as powerful in its manipulations as possible, it is recommended that the registration of functions be minimized to the simplest possible set, and thus registration should only be used when necessary. This is because any code within a registered function is treated as a blackbox imperative code that cannot be manipulated, thus decreasing the potential for simplifications.","category":"page"},{"location":"manual/functions/#Registering-Functions","page":"Function Registration and Tracing","title":"Registering Functions","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"The Symbolics graph only allows registered Julia functions within its type. All other functions are automatically traced down to registered functions. By default, Symbolics.jl pre-registers the common functions utilized in SymbolicUtils.jl and pre-defines their derivatives. However, the user can utilize the @register_symbolic macro to add their function to allowed functions of the computation graph.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Additionally, @register_array_symbolic can be used to define array functions. For size propagation it's required that a computation of how the sizes are computed is also supplied.","category":"page"},{"location":"manual/functions/#Defining-Derivatives-of-Registered-Functions","page":"Function Registration and Tracing","title":"Defining Derivatives of Registered Functions","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"In order for symbolic differentiation to work, an overload of Symbolics.derivative is required. The syntax is derivative(typeof(f), args::NTuple{i,Any}, ::Val{j}) where i is the number of arguments to the function and j is which argument is being differentiated. So for example:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"function derivative(::typeof(min), args::NTuple{2,Any}, ::Val{1})\n x, y = args\n IfElse.ifelse(x < y, one(x), zero(x))\nend","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"is the partial derivative of the Julia min(x,y) function with respect to x.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"note: Note\nDownstream symbolic derivative functionality only work if every partial derivative that is required in the derivative expression is defined. Note that you only need to define the partial derivatives which are actually computed.","category":"page"},{"location":"manual/functions/#Registration-of-Array-Functions","page":"Function Registration and Tracing","title":"Registration of Array Functions","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Similar to scalar functions, array functions can be registered to define new primitives for functions which either take in or return arrays. This is done by using the @register_array_symbolic macro. It acts similarly to the scalar function registration but requires a calculation of the input and output sizes. For example, let's assume we wanted to have a function that computes the solution to Ax = b, i.e. a linear solve, using an SVD factorization. In Julia, the code for this would be svdsolve(A,b) = svd(A)\\b. We would create this function as follows:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"using LinearAlgebra, Symbolics\n\nsvdsolve(A, b) = svd(A)\\b\n@register_array_symbolic svdsolve(A::AbstractMatrix, b::AbstractVector) begin\n size = size(b)\n eltype = promote_type(eltype(A), eltype(b))\nend","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Now using the function svdsolve with symbolic array variables will be kept lazy:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"@variables A[1:3, 1:3] b[1:3]\nsvdsolve(A,b)","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Note that at this time array derivatives cannot be defined.","category":"page"},{"location":"manual/functions/#Registration-API","page":"Function Registration and Tracing","title":"Registration API","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"@register_symbolic\n@register_array_symbolic","category":"page"},{"location":"manual/functions/#Symbolics.@register_symbolic","page":"Function Registration and Tracing","title":"Symbolics.@register_symbolic","text":"@register_symbolic(expr, define_promotion = true, Ts = [Real])\n\nOverload appropriate methods so that Symbolics can stop tracing into the registered function. If define_promotion is true, then a promotion method in the form of\n\nSymbolicUtils.promote_symtype(::typeof(f_registered), args...) = Real # or the annotated return type\n\nis defined for the register function. Note that when defining multiple register overloads for one function, all the rest of the registers must set define_promotion to false except for the first one, to avoid method overwriting.\n\nExamples\n\n@register_symbolic foo(x, y)\n@register_symbolic foo(x, y::Bool) false # do not overload a duplicate promotion rule\n@register_symbolic goo(x, y::Int) # `y` is not overloaded to take symbolic objects\n@register_symbolic hoo(x, y)::Int # `hoo` returns `Int`\n\nSee @register_array_symbolic to register functions which return arrays.\n\n\n\n\n\n","category":"macro"},{"location":"manual/functions/#Symbolics.@register_array_symbolic","page":"Function Registration and Tracing","title":"Symbolics.@register_array_symbolic","text":"@register_array_symbolic(expr, define_promotion = true)\n\nExample:\n\n# Let's say vandermonde takes an n-vector and returns an n x n matrix\n@register_array_symbolic vandermonde(x::AbstractVector) begin\n size=(length(x), length(x))\n eltype=eltype(x) # optional, will default to the promoted eltypes of x\nend\n\nYou can also register calls on callable structs:\n\n@register_array_symbolic (c::Conv)(x::AbstractMatrix) begin\n size=size(x) .- size(c.kernel) .+ 1\n eltype=promote_type(eltype(x), eltype(c))\nend\n\nIf define_promotion = true then a promotion method in the form of\n\nSymbolicUtils.promote_symtype(::typeof(f_registered), args...) = # inferred or annotated return type\n\nis defined for the register function. Note that when defining multiple register overloads for one function, all the rest of the registers must set define_promotion to false except for the first one, to avoid method overwriting.\n\n\n\n\n\n","category":"macro"},{"location":"manual/functions/#Direct-Registration-API-(Advanced,-Experimental)","page":"Function Registration and Tracing","title":"Direct Registration API (Advanced, Experimental)","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"warning: Warning\nThis is a lower level API which is not as stable as the macro APIs.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"In some circumstances you may need to use the direct API in order to define registration on functions or types without using the macro. This is done by directly defining dispatches on symbolic objects.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"A good example of this is DataInterpolations.jl's interpolations object. On an interpolation by a symbolic variable, we generate the symbolic function (the term) for the interpolation function. This looks like:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"using DataInterpolations, Symbolics, SymbolicUtils\n(interp::AbstractInterpolation)(t::Num) = SymbolicUtils.term(interp, unwrap(t))","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"In order for this to work, it is required that we define the symtype for the symbolic type inference. This is done via:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"SymbolicUtils.promote_symtype(t::AbstractInterpolation, args...) = Real","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Additionally a symbolic name is required:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Base.nameof(interp::AbstractInterpolation) = :Interpolation","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"The derivative is defined similarly to the macro case:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"function Symbolics.derivative(interp::AbstractInterpolation, args::NTuple{1, Any}, ::Val{1})\n Symbolics.unwrap(derivative(interp, Symbolics.wrap(args[1])))\nend","category":"page"},{"location":"manual/functions/#Inverse-function-registration","page":"Function Registration and Tracing","title":"Inverse function registration","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Symbolics.jl allows defining and querying the inverses of functions.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"inverse\nleft_inverse\nright_inverse\n@register_inverse\nhas_inverse\nhas_left_inverse\nhas_right_inverse","category":"page"},{"location":"manual/functions/#Symbolics.inverse","page":"Function Registration and Tracing","title":"Symbolics.inverse","text":"inverse(f)\n\nGiven a single-input single-output function f, return its inverse g. This requires that f is bijective. If inverse is defined for a function, left_inverse and right_inverse should return inverse(f). inverse(g) should also be defined to return f.\n\nSee also: left_inverse, right_inverse, @register_inverse.\n\n\n\n\n\n","category":"function"},{"location":"manual/functions/#Symbolics.left_inverse","page":"Function Registration and Tracing","title":"Symbolics.left_inverse","text":"left_inverse(f)\n\nGiven a single-input single-output function f, return its left inverse g. This requires that f is injective. If left_inverse is defined for a function, right_inverse and inverse must not be defined and should error. right_inverse(g) should also be defined to return f.\n\nSee also: inverse, right_inverse, @register_inverse.\n\n\n\n\n\n","category":"function"},{"location":"manual/functions/#Symbolics.right_inverse","page":"Function Registration and Tracing","title":"Symbolics.right_inverse","text":"right_inverse(f)\n\nGiven a single-input single-output function f, return its right inverse g. This requires that f is surjective. If right_inverse is defined for a function, left_inverse and inverse must not be defined and should error. left_inverse(g) should also be defined to return f.\n\nSee also inverse, left_inverse, @register_inverse.\n\n\n\n\n\n","category":"function"},{"location":"manual/functions/#Symbolics.@register_inverse","page":"Function Registration and Tracing","title":"Symbolics.@register_inverse","text":"@register_inverse f g\n@register_inverse f g left\n@register_inverse f g right\n\nMark f and g as inverses of each other. By default, assume that f and g are bijective. Also defines left_inverse and right_inverse to call inverse. If the third argument is left, assume that f is injective and g is its left inverse. If the third argument is right, assume that f is surjective and g is its right inverse.\n\n\n\n\n\n","category":"macro"},{"location":"manual/functions/#Symbolics.has_inverse","page":"Function Registration and Tracing","title":"Symbolics.has_inverse","text":"has_inverse(_) -> Bool\n\n\nCheck if the provided function has an inverse defined via inverse. Uses hasmethod to perform the check.\n\n\n\n\n\n","category":"function"},{"location":"manual/functions/#Symbolics.has_left_inverse","page":"Function Registration and Tracing","title":"Symbolics.has_left_inverse","text":"has_left_inverse(_) -> Bool\n\n\nCheck if the provided function has a left inverse defined via left_inverse Uses hasmethod to perform the check.\n\n\n\n\n\n","category":"function"},{"location":"manual/functions/#Symbolics.has_right_inverse","page":"Function Registration and Tracing","title":"Symbolics.has_right_inverse","text":"has_right_inverse(_) -> Bool\n\n\nCheck if the provided function has a left inverse defined via left_inverse Uses hasmethod to perform the check.\n\n\n\n\n\n","category":"function"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Symbolics.jl implements inverses for standard trigonometric and logarithmic functions, as well as their variants from NaNMath. It also implements inverses of ComposedFunctions.","category":"page"},{"location":"getting_started/#Getting-Started-with-Symbolics.jl","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.jl is a symbolic modeling language. In this tutorial, we will walk you through the process of getting Symbolics.jl up and running, and start doing our first symbolic calculations.","category":"page"},{"location":"getting_started/#Installing-Symbolics.jl","page":"Getting Started with Symbolics.jl","title":"Installing Symbolics.jl","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Installing Symbolics.jl is as simple as using the Julia package manager. This is done via the command ]add Symbolics. After precompilation is complete, do using Symbolics in the terminal (REPL) and when that command is completed, you're ready to start!","category":"page"},{"location":"getting_started/#Building-Symbolic-Expressions","page":"Getting Started with Symbolics.jl","title":"Building Symbolic Expressions","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"The way to define symbolic variables is via the @variables macro:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using Symbolics\n@variables x y","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"After defining variables as symbolic, symbolic expressions, which we call a iscall object, can be generated by utilizing Julia expressions. For example:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"z = x^2 + y","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Here, z is an expression tree for “square x and add y”. To make an array of symbolic expressions, simply make an array of symbolic expressions:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"A = [x^2 + y 0 2x\n 0 0 2y\n y^2 + x 0 0]","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Note that by default, @variables returns Sym or iscall objects wrapped in Num to make them behave like subtypes of Real. Any operation on these Num objects will return a new Num object, wrapping the result of computing symbolically on the underlying values.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"If you are following this tutorial in the Julia REPL, A will not be shown with LaTeX equations. To get these equations, we can use Latexify.jl. Symbolics.jl comes with Latexify recipes, so it works automatically:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using Latexify\nlatexify(A)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Normal Julia functions work on Symbolics expressions, so if we want to create the sparse version of A we would just call sparse:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using SparseArrays\nspA = sparse(A)\nlatexify(A)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"We can thus use normal Julia functions as generators for sparse expressions. For example, here we will define","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"function f(u)\n [u[1] - u[3], u[1]^2 - u[2], u[3] + u[2]]\nend\nf([x, y, z]) # Recall that z = x^2 + y","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Or we can build an array of variables and use it to trace the function:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"u = Symbolics.variables(:u, 1:5)\nf(u)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"note: Note\nSymbolics.variables(:u, 1:5) creates a Julia array of symbolic variables. This uses O(n) compute and memory but is a very general representation. Symbolics.jl also has the ability to represent symbolic arrays which gives an O(1) representation but is more limited in its functionality. For more information, see the Symbolic Arrays page.","category":"page"},{"location":"getting_started/#Derivatives","page":"Getting Started with Symbolics.jl","title":"Derivatives","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"One common thing to compute in a symbolic system is derivatives. In Symbolics.jl, derivatives are represented lazily via operations, just like any other function. To build a differential operator, use Differential like:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"@variables t\nD = Differential(t)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"This is the differential operator D = fracpartialpartial t. We can compose the differential operator by *, e.g. Differential(t) * Differential(x) or Differential(t)^2. Now let's write down the derivative of some expression:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"z = t + t^2\nD(z)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Notice that this hasn't computed anything yet: D is a lazy operator because it lets us symbolically represent “The derivative of z with respect to t”, which is useful for example when representing our favorite thing in the world, differential equations. However, if we want to expand the derivative operators, we'd use expand_derivatives:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"expand_derivatives(D(z))","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"The variable, that you are taking the derivative with respect to, is accessed with:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"D.x","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"We can also have simplified functions for multivariable calculus. For example, we can compute the Jacobian of an array of expressions like:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.jacobian([x + x*y, x^2 + y], [x, y])","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"and similarly we can do Hessians, gradients, and define whatever other derivatives you want.","category":"page"},{"location":"getting_started/#Simplification-and-Substitution","page":"Getting Started with Symbolics.jl","title":"Simplification and Substitution","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics interfaces with SymbolicUtils.jl to allow for simplifying symbolic expressions. This is done simply through the simplify command:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"simplify(2x + 2y)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"This can be applied to arrays by using Julia's broadcast mechanism:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"B = simplify.([t + t^2 + t + t^2 2t + 4t\n x + y + y + 2t x^2 - x^2 + y^2])","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"We can then use substitute to change values of an expression around:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"simplify.(substitute.(B, (Dict(x => y^2),)))","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"and we can use this to interactively evaluate expressions without generating and compiling Julia functions:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"V = substitute.(B, (Dict(x => 2.0, y => 3.0, t => 4.0),))","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Where we can reference the values via:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.value.(V)","category":"page"},{"location":"getting_started/#Non-Interactive-Development","page":"Getting Started with Symbolics.jl","title":"Non-Interactive Development","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Note that the macros are for the high-level case where you're doing symbolic computation on your own code. If you want to do symbolic computation on someone else's code, like in a macro, you may not want to do @variables x because you might want the name “x” to come from the user's code. For these cases, you can use the interpolation operator to interpolate the runtime value of x, i.e. @variables $x. Check the documentation of @variables for more details.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"a, b, c = :runtime_symbol_value, :value_b, :value_c","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"vars = @variables t $a $b(t) $c(t)[1:3]","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"(t, a, b, c)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"One could also use variable and variables. Read their documentation for more details.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"If we need to use this to generate new Julia code, we can simply convert the output to an Expr:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.toexpr(x + y^2)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"The other way around is also possible, parsing Julia expressions into symbolic expressions","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"ex = [:(v ~ w)\n :(w ~ -v)]\neqs = parse_expr_to_symbolic.(ex, (Main,))\neqs_lhs = [eq.lhs for eq in eqs]\neqs_rhs = [eq.rhs for eq in eqs]\nSymbolics.jacobian(eqs_rhs, eqs_lhs)","category":"page"},{"location":"getting_started/#Syms-and-callable-Syms","page":"Getting Started with Symbolics.jl","title":"Syms and callable Syms","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"In the definition","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"@variables t x(t) y(t)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"t is of type Sym{Real}, but the name x refers to an object that represents the Term x(t). The operation of this expression is itself the object Sym{FnType{Tuple{Real}, Real}}(:x). The type Sym{FnType{...}} represents a callable object. In this case specifically, it's a function that takes 1 Real argument (noted by Tuple{Real}) and returns a Real result. You can call such a callable Sym with either a number or a symbolic expression of a permissible type.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"This expression also defines t as an independent variable, while x(t) and y(t) are dependent variables. This is accounted for in differentiation:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"z = x + y*t\nexpand_derivatives(D(z))","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Since x and y are time-dependent, they are not automatically eliminated from the expression, and thus the D(x) and D(y) operations still exist in the expanded derivatives for correctness.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"We can also define unrestricted functions:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"@variables g(..)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Here, g is a variable that is a function of other variables. Any time that we reference g we have to utilize it as a function:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"z = g(x) + g(y)","category":"page"},{"location":"getting_started/#Registering-Functions","page":"Getting Started with Symbolics.jl","title":"Registering Functions","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"One of the benefits of a one-language Julia symbolic stack is that the primitives are all written in Julia, and therefore it's trivially extendable from Julia itself. By default, new functions are traced to the primitives and the symbolic expressions are written on the primitives. However, we can expand the allowed primitives by registering new functions. For example, let's register a new function h:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"h(x, y) = x^2 + y\n@register_symbolic h(x, y)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"When we use h(x, y), it is a symbolic expression and doesn't expand:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"h(x, y) + y^2","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"To use it with the differentiation system, we need to register its derivatives. We would do it like this:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"# Derivative w.r.t. the first argument\nSymbolics.derivative(::typeof(h), args::NTuple{2,Any}, ::Val{1}) = 2args[1]\n# Derivative w.r.t. the second argument\nSymbolics.derivative(::typeof(h), args::NTuple{2,Any}, ::Val{2}) = 1","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"and now it works with the rest of the system:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.derivative(h(x, y) + y^2, x)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.derivative(h(x, y) + y^2, y)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"note: Note\n@register_symbolic only allows for scalar outputs. If full array functions are needed, then see @register_array_symbolic for registering functions of symbolic arrays.","category":"page"},{"location":"getting_started/#Building-Functions","page":"Getting Started with Symbolics.jl","title":"Building Functions","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"The function for building functions from symbolic expressions is the aptly-named build_function. The first argument is the symbolic expression or the array of symbolic expressions to compile, and the trailing arguments are the arguments for the function. For example:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"to_compute = [x^2 + y, y^2 + x]\nf_expr = build_function(to_compute, [x, y])\nBase.remove_linenums!.(f_expr)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"gives back two codes. The first is a function f([x, y]) that computes and builds an output vector [x^2 + y, y^2 + x]. Because this tool was made to be used by all the cool kids writing fast Julia codes, it is specialized to Julia and supports features like StaticArrays. For example:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using StaticArrays\nmyf = eval(f_expr[1])\nmyf(SA[2.0, 3.0])","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"The second function is an in-place non-allocating mutating function which mutates its first value. Thus, we'd use it like the following:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"myf! = eval(f_expr[2])\nout = zeros(2)\nmyf!(out, [2.0, 3.0])\nout","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"To save the symbolic calculations for later, we can take this expression and save it out to a file:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"write(\"function.jl\", string(f_expr[2]))","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Note that if we need to avoid eval, for example to avoid world-age issues, one could do expression = Val{false}:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Base.remove_linenums!(build_function(to_compute, [x, y], expression=Val{false})[1])","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"which will use RuntimeGeneratedFunctions.jl to build Julia functions which avoid world-age issues.","category":"page"},{"location":"getting_started/#Building-Non-Allocating-Parallel-Functions-for-Sparse-Matrices","page":"Getting Started with Symbolics.jl","title":"Building Non-Allocating Parallel Functions for Sparse Matrices","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Now let's show off a little bit. build_function is kind of like if lambdify ate its spinach. To show this, let's build a non-allocating function that fills sparse matrices in a multithreaded manner. To do this, we just have to represent the operation that we're turning into a function via a sparse matrix. For example:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using LinearAlgebra\nN = 8\nA = sparse(Tridiagonal([x^i for i in 1:N-1], [x^i * y^(8-i) for i in 1:N], [y^i for i in 1:N-1]))\nshow(A)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Now we call build_function:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Base.remove_linenums!(build_function(A,[x,y],parallel=Symbolics.MultithreadedForm())[2])","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Let's unpack what that's doing. It's using A.nzval to linearly index through the sparse matrix, avoiding the A[i,j] form because that is a more expensive way to index a sparse matrix if you know exactly the order that the data is stored. Then, it's splitting up the calculation into Julia threads, so they can be operated on in parallel. It synchronizes after spawning all the tasks, so the computation is ensured to be complete before moving on. And it does this with all in-place operations to the original matrix instead of generating arrays. This is the fanciest way you could fill a sparse matrix, and Symbolics makes this dead simple.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"(Note: this example may be slower with multithreading due to the thread spawning overhead, but the full version was not included in the documentation for brevity. It will be the faster version if N is sufficiently large!)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Importantly, when using the in-place functions generated by build_function, it is important that the mutating argument matches the sparse structure of the original function, as demonstrated below.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using Symbolics, SparseArrays, LinearAlgebra\n\nN = 10\n_S = sprand(N, N, 0.1)\n_Q = sprand(N, N, 0.1)\n\nF(z) = [ # some complicated sparse amenable function\n _S * z\n _Q * z.^2\n]\n\nSymbolics.@variables z[1:N]\n\nsj = Symbolics.sparsejacobian(F(z), z) # sparse jacobian\n\nf_expr = build_function(sj, z)\n\nrows, cols, _ = findnz(sj)\nout = sparse(rows, cols, zeros(length(cols)), size(sj)...) # pre-allocate, and correct structure\nmyf = eval(last(f_expr))\nmyf(out, rand(N)) # note that out matches the sparsity structure of sj\nout","category":"page"},{"location":"manual/expression_manipulation/#Expression-Manipulation","page":"Expression Manipulation","title":"Expression Manipulation","text":"","category":"section"},{"location":"manual/expression_manipulation/","page":"Expression Manipulation","title":"Expression Manipulation","text":"Symbolics.jl provides functionality for easily manipulating expressions. Most of the functionality comes by the expression objects obeying the standard mathematical semantics. For example, if one has A a matrix of symbolic expressions wrapped in Num, then A^2 calculates the expressions for the squared matrix. It is thus encouraged to use standard Julia for performing many of the manipulation on the IR. For example, calculating the sparse form of the matrix via sparse(A) is valid, legible, and easily understandable to all Julia programmers.","category":"page"},{"location":"manual/expression_manipulation/#Functionality-Inherited-From-SymbolicUtils.jl","page":"Expression Manipulation","title":"Functionality Inherited From SymbolicUtils.jl","text":"","category":"section"},{"location":"manual/expression_manipulation/","page":"Expression Manipulation","title":"Expression Manipulation","text":"SymbolicUtils.substitute\nSymbolicUtils.simplify","category":"page"},{"location":"manual/expression_manipulation/#SymbolicUtils.substitute","page":"Expression Manipulation","title":"SymbolicUtils.substitute","text":"substitute(expr, s; fold=true)\n\nPerforms the substitution on expr according to rule(s) s. If fold=false, expressions which can be evaluated won't be evaluated.\n\nExamples\n\njulia> @variables t x y z(t)\n4-element Vector{Num}:\n t\n x\n y\n z(t)\njulia> ex = x + y + sin(z)\n(x + y) + sin(z(t))\njulia> substitute(ex, Dict([x => z, sin(z) => z^2]))\n(z(t) + y) + (z(t) ^ 2)\njulia> substitute(sqrt(2x), Dict([x => 1]); fold=false)\nsqrt(2)\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#SymbolicUtils.simplify","page":"Expression Manipulation","title":"SymbolicUtils.simplify","text":"simplify(x; expand=false,\n threaded=false,\n thread_subtree_cutoff=100,\n rewriter=nothing)\n\nSimplify an expression (x) by applying rewriter until there are no changes. expand=true applies expand in the beginning of each fixpoint iteration.\n\nBy default, simplify will assume denominators are not zero and allow cancellation in fractions. Pass simplify_fractions=false to prevent this.\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/","page":"Expression Manipulation","title":"Expression Manipulation","text":"Documentation for rewriter can be found here, using the @rule macro or the @acrule macro from SymbolicUtils.jl.","category":"page"},{"location":"manual/expression_manipulation/#Additional-Manipulation-Functions","page":"Expression Manipulation","title":"Additional Manipulation Functions","text":"","category":"section"},{"location":"manual/expression_manipulation/","page":"Expression Manipulation","title":"Expression Manipulation","text":"Other additional manipulation functions are given below.","category":"page"},{"location":"manual/expression_manipulation/","page":"Expression Manipulation","title":"Expression Manipulation","text":"Symbolics.get_variables\nSymbolics.tosymbol\nSymbolics.diff2term\nSymbolics.degree\nSymbolics.coeff\nSymbolics.replace\nSymbolics.occursin\nSymbolics.filterchildren\nSymbolics.fixpoint_sub\nSymbolics.fast_substitute\nSymbolics.symbolic_to_float","category":"page"},{"location":"manual/expression_manipulation/#Symbolics.get_variables","page":"Expression Manipulation","title":"Symbolics.get_variables","text":"get_variables(e, varlist = nothing; sort::Bool = false)\n\nReturn a vector of variables appearing in e, optionally restricting to variables in varlist.\n\nNote that the returned variables are not wrapped in the Num type.\n\nExamples\n\njulia> @variables t x y z(t);\n\njulia> Symbolics.get_variables(x + y + sin(z); sort = true)\n3-element Vector{SymbolicUtils.BasicSymbolic}:\n x\n y\n z(t)\n\njulia> Symbolics.get_variables(x - y; sort = true)\n2-element Vector{SymbolicUtils.BasicSymbolic}:\n x\n y\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.tosymbol","page":"Expression Manipulation","title":"Symbolics.tosymbol","text":"tosymbol(x::Union{Num,Symbolic}; states=nothing, escape=true) -> Symbol\n\nConvert x to a symbol. states are the states of a system, and escape means if the target has escapes like val\"y(t)\". If escape is false, then it will only output y instead of y(t).\n\nExamples\n\njulia> @variables t z(t)\n2-element Vector{Num}:\n t\n z(t)\n\njulia> Symbolics.tosymbol(z)\nSymbol(\"z(t)\")\n\njulia> Symbolics.tosymbol(z; escape=false)\n:z\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.diff2term","page":"Expression Manipulation","title":"Symbolics.diff2term","text":"diff2term(x, x_metadata::Dict{Datatype, Any}) -> Symbolic\n\nConvert a differential variable to a Term. Note that it only takes a Term not a Num. Any upstream metadata can be passed via x_metadata\n\njulia> @variables x t u(x, t) z(t)[1:2]; Dt = Differential(t); Dx = Differential(x);\n\njulia> Symbolics.diff2term(Symbolics.value(Dx(Dt(u))))\nuˍtx(x, t)\n\njulia> Symbolics.diff2term(Symbolics.value(Dt(z[1])))\nvar\"z(t)[1]ˍt\"\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.degree","page":"Expression Manipulation","title":"Symbolics.degree","text":"degree(p, sym=nothing)\n\nExtract the degree of p with respect to sym.\n\nExamples\n\njulia> @variables x;\n\njulia> Symbolics.degree(x^0)\n0\n\njulia> Symbolics.degree(x)\n1\n\njulia> Symbolics.degree(x^2)\n2\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.coeff","page":"Expression Manipulation","title":"Symbolics.coeff","text":"coeff(p, sym=nothing)\n\nExtract the coefficient of p with respect to sym. Note that p might need to be expanded and/or simplified with expand and/or simplify.\n\nExamples\n\njulia> @variables a x y;\n\njulia> Symbolics.coeff(2a, x)\n0\n\njulia> Symbolics.coeff(3x + 2y, y)\n2\n\njulia> Symbolics.coeff(x^2 + y, x^2)\n1\n\njulia> Symbolics.coeff(2*x*y + y, x*y)\n2\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Base.occursin","page":"Expression Manipulation","title":"Base.occursin","text":"occursin(needle::Symbolic, haystack::Symbolic)\n\nDetermine whether the second argument contains the first argument. Note that this function doesn't handle associativity, commutativity, or distributivity.\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.filterchildren","page":"Expression Manipulation","title":"Symbolics.filterchildren","text":"filterchildren(c, x)\n\nReturns all parts of x that fulfills the condition given in c. c can be a function or an expression. If it is a function, returns everything for which the function is true. If c is an expression, returns all expressions that matches it.\n\nExamples:\n\n@syms x\nSymbolics.filterchildren(x, log(x) + x + 1)\n\nreturns [x, x]\n\n@variables t X(t)\nD = Differential(t)\nSymbolics.filterchildren(Symbolics.is_derivative, X + D(X) + D(X^2))\n\nreturns [Differential(t)(X(t)^2), Differential(t)(X(t))]\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.fixpoint_sub","page":"Expression Manipulation","title":"Symbolics.fixpoint_sub","text":"fixpoint_sub(expr, dict; operator = Nothing, maxiters = 10000)\n\nGiven a symbolic expression, equation or inequality expr perform the substitutions in dict recursively until the expression does not change. Substitutions that depend on one another will thus be recursively expanded. For example, fixpoint_sub(x, Dict(x => y, y => 3)) will return 3. The operator keyword can be specified to prevent substitution of expressions inside operators of the given type. The maxiters keyword is used to limit the number of times the substitution can occur to avoid infinite loops in cases where the substitutions in dict are circular (e.g. [x => y, y => x]).\n\nSee also: fast_substitute.\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.fast_substitute","page":"Expression Manipulation","title":"Symbolics.fast_substitute","text":"fast_substitute(expr, dict; operator = Nothing)\n\nGiven a symbolic expression, equation or inequality expr perform the substitutions in dict. This only performs the substitutions once. For example, fast_substitute(x, Dict(x => y, y => 3)) will return y. The operator keyword can be specified to prevent substitution of expressions inside operators of the given type.\n\nSee also: fixpoint_sub.\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.symbolic_to_float","page":"Expression Manipulation","title":"Symbolics.symbolic_to_float","text":"symbolic_to_float(x::Union{Num, BasicSymbolic})::Union{AbstractFloat, BasicSymbolic}\n\nIf the symbolic value is exactly equal to a number, converts the symbolic value to a floating point number. Otherwise retains the symbolic value.\n\nExamples\n\nsymbolic_to_float((1//2 * x)/x) # 0.5\nsymbolic_to_float((1/2 * x)/x) # 0.5\nsymbolic_to_float((1//2)*√(279//4)) # 4.175823272122517\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Derivatives-and-Differentials","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"","category":"section"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"A Differential(op) is a partial derivative with respect to op, which can then be applied to some other operations. For example, D=Differential(t) is what would commonly be referred to as d/dt, which can then be applied to other operations using its function call, so D(x+y) is d(x+y)/dt.","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"By default, the derivatives are left unexpanded to capture the symbolic representation of the differential equation. If the user would like to expand out all the differentials, the expand_derivatives function eliminates all the differentials down to basic one-variable expressions.","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"Differential\nexpand_derivatives\nis_derivative","category":"page"},{"location":"manual/derivatives/#Symbolics.Differential","page":"Derivatives and Differentials","title":"Symbolics.Differential","text":"struct Differential <: Symbolics.Operator\n\nRepresents a differential operator.\n\nFields\n\nx: The variable or expression to differentiate with respect to.\n\nExamples\n\njulia> using Symbolics\n\njulia> @variables x y;\n\njulia> D = Differential(x)\n(D'~x)\n\njulia> D(y) # Differentiate y wrt. x\n(D'~x)(y)\n\njulia> Dx = Differential(x) * Differential(y) # d^2/dxy operator\n(D'~x(t)) ∘ (D'~y(t))\n\njulia> D3 = Differential(x)^3 # 3rd order differential operator\n(D'~x(t)) ∘ (D'~x(t)) ∘ (D'~x(t))\n\n\n\n\n\n","category":"type"},{"location":"manual/derivatives/#Symbolics.expand_derivatives","page":"Derivatives and Differentials","title":"Symbolics.expand_derivatives","text":"expand_derivatives(O)\nexpand_derivatives(O, simplify)\n\n\nExpands derivatives within a symbolic expression O.\n\nThis function recursively traverses a symbolic expression, applying the chain rule and other derivative rules to expand any derivatives it encounters.\n\nArguments\n\nO::Symbolic: The symbolic expression to expand.\nsimplify::Bool=false: Whether to simplify the resulting expression using SymbolicUtils.simplify.\n\nExamples\n\njulia> @variables x y z k;\n\njulia> f = k*(abs(x-y)/y-z)^2\nk*((abs(x - y) / y - z)^2)\n\njulia> Dx = Differential(x) # Differentiate wrt x\n(::Differential) (generic function with 2 methods)\n\njulia> dfx = expand_derivatives(Dx(f))\n(k*((2abs(x - y)) / y - 2z)*IfElse.ifelse(signbit(x - y), -1, 1)) / y\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"note: Note\nFor symbolic differentiation, all registered functions in the symbolic expression need a registered derivative. For more information, see the function registration page.","category":"page"},{"location":"manual/derivatives/#High-Level-Differentiation-Functions","page":"Derivatives and Differentials","title":"High-Level Differentiation Functions","text":"","category":"section"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"The following functions are not exported and thus must be accessed in a namespaced way, i.e. Symbolics.jacobian.","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"Symbolics.derivative\nSymbolics.jacobian\nSymbolics.sparsejacobian\nSymbolics.sparsejacobian_vals\nSymbolics.gradient\nSymbolics.hessian\nSymbolics.sparsehessian\nSymbolics.sparsehessian_vals","category":"page"},{"location":"manual/derivatives/#Symbolics.derivative","page":"Derivatives and Differentials","title":"Symbolics.derivative","text":"derivative(O, var; simplify)\n\n\nA helper function for computing the derivative of the expression O with respect to var.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.jacobian","page":"Derivatives and Differentials","title":"Symbolics.jacobian","text":"jacobian(ops, vars; simplify, scalarize)\n\n\nA helper function for computing the Jacobian of an array of expressions with respect to an array of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.sparsejacobian","page":"Derivatives and Differentials","title":"Symbolics.sparsejacobian","text":"sparsejacobian(ops, vars; simplify)\n\n\nA helper function for computing the sparse Jacobian of an array of expressions with respect to an array of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.sparsejacobian_vals","page":"Derivatives and Differentials","title":"Symbolics.sparsejacobian_vals","text":"sparsejacobian_vals(ops, vars, I, J; simplify)\n\n\nA helper function for computing the values of the sparse Jacobian of an array of expressions with respect to an array of variable expressions given the sparsity structure.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.gradient","page":"Derivatives and Differentials","title":"Symbolics.gradient","text":"gradient(O, vars; simplify)\n\n\nA helper function for computing the gradient of the expression O with respect to an array of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.hessian","page":"Derivatives and Differentials","title":"Symbolics.hessian","text":"hessian(O, vars; simplify)\n\n\nA helper function for computing the Hessian of the expression O with respect to an array of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.sparsehessian","page":"Derivatives and Differentials","title":"Symbolics.sparsehessian","text":"sparsehessian(op, vars; simplify, full)\n\n\nA helper function for computing the sparse Hessian of an expression with respect to an array of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.sparsehessian_vals","page":"Derivatives and Differentials","title":"Symbolics.sparsehessian_vals","text":"sparsehessian_vals(op, vars, I, J; simplify)\n\n\nA helper function for computing the values of the sparse Hessian of an expression with respect to an array of variable expressions given the sparsity structure.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Adding-Analytical-Derivatives","page":"Derivatives and Differentials","title":"Adding Analytical Derivatives","text":"","category":"section"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"There are many derivatives pre-defined by DiffRules.jl. For example,","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"using Symbolics\n@variables x y z\nf(x,y,z) = x^2 + sin(x+y) - z","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"f automatically has the derivatives defined via the tracing mechanism. It will do this by directly building the internals of your function and differentiating that.","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"However, often you may want to define your own derivatives so that way automatic Jacobian etc. calculations can utilize this information. This can allow for more succinct versions of the derivatives to be calculated to scale to larger systems. You can define derivatives for your function via the dispatch:","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"# `N` arguments are accepted by the relevant method of `my_function`\nSymbolics.derivative(::typeof(my_function), args::NTuple{N,Any}, ::Val{i})","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"where i means that it's the derivative with respect to the ith argument. args is the array of arguments, so, for example, if your function is f(x,t), then args = [x,t]. You should return an Term for the derivative of your function.","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"For example, sin(t)'s derivative (by t) is given by the following:","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"Symbolics.derivative(::typeof(sin), args::NTuple{1,Any}, ::Val{1}) = cos(args[1])","category":"page"},{"location":"manual/taylor/#Taylor-Series","page":"Taylor Series","title":"Taylor Series","text":"","category":"section"},{"location":"manual/taylor/","page":"Taylor Series","title":"Taylor Series","text":"For a real example of how to use the Taylor series functionality, see this tutorial.","category":"page"},{"location":"manual/taylor/","page":"Taylor Series","title":"Taylor Series","text":"series\ntaylor\ntaylor_coeff","category":"page"},{"location":"manual/taylor/#Symbolics.series","page":"Taylor Series","title":"Symbolics.series","text":"series(cs, x, [x0=0,], ns=0:length(cs)-1)\n\nReturn the power series in x around x0 to the powers ns with coefficients cs.\n\nseries(y, x, [x0=0,] ns)\n\nReturn the power series in x around x0 to the powers ns with coefficients automatically created from the variable y.\n\nExamples\n\njulia> @variables x y[0:3] z\n3-element Vector{Any}:\n x\n y[0:3]\n z\n\njulia> series(y, x, 2)\ny[0] + (-2 + x)*y[1] + ((-2 + x)^2)*y[2] + ((-2 + x)^3)*y[3]\n\njulia> series(z, x, 2, 0:3)\nz[0] + (-2 + x)*z[1] + ((-2 + x)^2)*z[2] + ((-2 + x)^3)*z[3]\n\n\n\n\n\n","category":"function"},{"location":"manual/taylor/#Symbolics.taylor","page":"Taylor Series","title":"Symbolics.taylor","text":"taylor(f, x, [x0=0,] n; rationalize=true)\n\nCalculate the n-th order term(s) in the Taylor series of f around x = x0. If rationalize, float coefficients are approximated as rational numbers (this can produce unexpected results for irrational numbers, for example).\n\nExamples\n\njulia> @variables x\n1-element Vector{Num}:\n x\n\njulia> taylor(exp(x), x, 0:3)\n1 + x + (1//2)*(x^2) + (1//6)*(x^3)\n\njulia> taylor(exp(x), x, 0:3; rationalize=false)\n1.0 + x + 0.5(x^2) + 0.16666666666666666(x^3)\n\njulia> taylor(√(x), x, 1, 0:3)\n1 + (1//2)*(-1 + x) - (1//8)*((-1 + x)^2) + (1//16)*((-1 + x)^3)\n\njulia> isequal(taylor(exp(im*x), x, 0:5), taylor(exp(im*x), x, 0:5))\ntrue\n\n\n\n\n\n","category":"function"},{"location":"manual/taylor/#Symbolics.taylor_coeff","page":"Taylor Series","title":"Symbolics.taylor_coeff","text":"taylor_coeff(f, x[, n]; rationalize=true)\n\nCalculate the n-th order coefficient(s) in the Taylor series of f around x = 0.\n\nExamples\n\njulia> @variables x y\n2-element Vector{Num}:\n x\n y\n\njulia> taylor_coeff(series(y, x, 0:5), x, 0:2:4)\n3-element Vector{Num}:\n y[0]\n y[2]\n y[4]\n\n\n\n\n\n","category":"function"},{"location":"#Symbolics.jl","page":"Home","title":"Symbolics.jl","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Symbolics.jl is a fast and modern Computer Algebra System (CAS) for a fast and modern programming language (Julia). The goal is to have a high-performance and parallelized symbolic algebra system that is directly extendable in the same language as that of the users.","category":"page"},{"location":"#Installation","page":"Home","title":"Installation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"To install Symbolics.jl, use the Julia package manager:","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg\nPkg.add(\"Symbolics\")","category":"page"},{"location":"#Citation","page":"Home","title":"Citation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"If you use Symbolics.jl, please cite this paper","category":"page"},{"location":"","page":"Home","title":"Home","text":"@article{gowda2021high,\n title={High-performance symbolic-numerics via multiple dispatch},\n author={Gowda, Shashi and Ma, Yingbo and Cheli, Alessandro and Gwozdz, Maja and Shah, Viral B and Edelman, Alan and Rackauckas, Christopher},\n journal={arXiv preprint arXiv:2105.03949},\n year={2021}\n}","category":"page"},{"location":"#Feature-Summary","page":"Home","title":"Feature Summary","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Because Symbolics.jl is built into the Julia language and works with its dispatches, generic functions in Base Julia will work with symbolic expressions! Make matrices of symbolic expressions and multiply them: it will just work. Take the LU-factorization. Etc. Thus, see the Julia Documentation for a large list of functionality available in Symbolics.jl.","category":"page"},{"location":"","page":"Home","title":"Home","text":"A general list of the features is:","category":"page"},{"location":"","page":"Home","title":"Home","text":"Symbolic arithmetic with type information and multiple dispatch\nSymbolic polynomials and trigonometric functions\nPattern matching, simplification and substitution\nDifferentiation\nSymbolic linear algebra (factorizations, inversion, determinants, eigencomputations, etc.)\nDiscrete math (representations of summations, products, binomial coefficients, etc.)\nLogical and Boolean expressions\nSymbolic equation solving and conversion to arbitrary precision\nSupport for non-standard algebras (non-commutative symbols and customizable rulesets)\nSpecial functions (list provided by SpecialFunctions.jl)\nAutomatic conversion of Julia code to symbolic code\nGeneration of (high performance and parallel) functions from symbolic expressions\nFast automated sparsity detection and generation of sparse Jacobians and Hessians","category":"page"},{"location":"","page":"Home","title":"Home","text":"and much more.","category":"page"},{"location":"#Extension-Packages","page":"Home","title":"Extension Packages","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Below is a list of known extension packages. If you would like your package to be listed here, feel free to open a pull request!","category":"page"},{"location":"","page":"Home","title":"Home","text":"ModelingToolkit.jl: Symbolic representations of common numerical systems\nOrdinary differential equations\nStochastic differential equations\nPartial differential equations\nNonlinear systems\nOptimization problems\nOptimal Control\nCausal and acausal modeling (Simulink/Modelica)\nAutomated model transformation, simplification, and composition\nCatalyst.jl: Symbolic representations of chemical reactions\nSymbolically build and represent large systems of chemical reactions\nGenerate code for ODEs, SDEs, continuous-time Markov Chains, and more\nSimulate the models using the SciML ecosystem with O(1) Gillespie methods\nDataDrivenDiffEq.jl: Automatic identification of equations from data\nAutomated construction of ODEs and DAEs from data\nRepresentations of Koopman operators and Dynamic Mode Decomposition (DMD)\nSymbolicRegression.jl: Distributed High-Performance symbolic regression\nParallelized generic algorithms for finding equations from data\nPareto frontier-based scoring\nReversePropagation.jl: Source-to-source reverse mode automatic differentiation\nAutomated tracing of code and construction of backpropagation equations\nComposes with symbolic transformation and simplification functionality","category":"page"},{"location":"#Reproducibility","page":"Home","title":"Reproducibility","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"
The documentation of this SciML package was built using these direct dependencies,","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg # hide\nPkg.status() # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"
and using this machine and Julia version.","category":"page"},{"location":"","page":"Home","title":"Home","text":"using InteractiveUtils # hide\nversioninfo() # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"
A more complete overview of all dependencies and their versions is also provided.","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg # hide\nPkg.status(; mode = PKGMODE_MANIFEST) # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"using TOML\nusing Markdown\nversion = TOML.parse(read(\"../../Project.toml\", String))[\"version\"]\nname = TOML.parse(read(\"../../Project.toml\", String))[\"name\"]\nlink_manifest = \"https://github.com/SciML/\" * name * \".jl/tree/gh-pages/v\" * version *\n \"/assets/Manifest.toml\"\nlink_project = \"https://github.com/SciML/\" * name * \".jl/tree/gh-pages/v\" * version *\n \"/assets/Project.toml\"\nMarkdown.parse(\"\"\"You can also download the\n[manifest]($link_manifest)\nfile and the\n[project]($link_project)\nfile.\n\"\"\")","category":"page"}] } diff --git a/dev/tutorials/auto_parallel/d7a609b7.svg b/dev/tutorials/auto_parallel/7d3ddc89.svg similarity index 58% rename from dev/tutorials/auto_parallel/d7a609b7.svg rename to dev/tutorials/auto_parallel/7d3ddc89.svg index 502404418..8bfc3878a 100644 --- a/dev/tutorials/auto_parallel/d7a609b7.svg +++ b/dev/tutorials/auto_parallel/7d3ddc89.svg @@ -1,13222 +1,13222 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/tutorials/auto_parallel/index.html b/dev/tutorials/auto_parallel/index.html index a972a5c6d..00d737509 100644 --- a/dev/tutorials/auto_parallel/index.html +++ b/dev/tutorials/auto_parallel/index.html @@ -59,7 +59,7 @@ \]

The output, here the in-place modified du, is a symbolic representation of each output of the function. We can then utilize this in the Symbolics functionality. For example, let's build a parallel version of f first:

fastf = eval(Symbolics.build_function(du,u,
             parallel=Symbolics.MultithreadedForm())[2])
#13 (generic function with 1 method)

Now let's compute the sparse Jacobian function and compile a fast multithreaded version:

jac = Symbolics.sparsejacobian(vec(du), vec(u))
 row,col,val = findnz(jac)
-scatter(row,col,legend=false,ms=1,c=:black)
Example block output
fjac = eval(Symbolics.build_function(jac,u,
+scatter(row,col,legend=false,ms=1,c=:black)
Example block output
fjac = eval(Symbolics.build_function(jac,u,
             parallel=Symbolics.MultithreadedForm())[2])
#15 (generic function with 1 method)

It takes awhile for this to generate, but the results will be worth it! Now let's set up the parabolic PDE to be solved by DifferentialEquations.jl. We will set up the vanilla version and the sparse multithreaded version:

using OrdinaryDiffEq
 u0 = zeros(N, N, 3)
 MyA = zeros(N, N);
@@ -139,4 +139,4 @@
  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0     0.0  0.0  0.0  0.0  0.0  0.0  0.0

Let's see the timing difference:

using BenchmarkTools
 #@btime solve(prob, TRBDF2()); # 33.073 s (895404 allocations: 23.87 GiB)
 #warning the following solve takes a long time to compile, but afterwards is very fast.
-#@btime solve(fastprob, TRBDF2()); # 209.670 ms (8208 allocations: 109.25 MiB)

Boom, an automatic 157x acceleration that grows as the size of the problem increases!

+#@btime solve(fastprob, TRBDF2()); # 209.670 ms (8208 allocations: 109.25 MiB)

Boom, an automatic 157x acceleration that grows as the size of the problem increases!

diff --git a/dev/tutorials/converting_to_C/index.html b/dev/tutorials/converting_to_C/index.html index 708c0ddd8..a02189afc 100644 --- a/dev/tutorials/converting_to_C/index.html +++ b/dev/tutorials/converting_to_C/index.html @@ -18,11 +18,11 @@ \end{equation} \]

and then we build the function:

build_function(du, u, p, t, target=Symbolics.CTarget())
"#include <math.h>\nvoid diffeqf(double* du, const double* RHS1, const double* RHS2, const double RHS3) {\n  du[0] = RHS2[0] * RHS1[0] + -1 * RHS2[1] * RHS1[0] * RHS1[1];\n  du[1] = -1 * RHS2[2] * RHS1[1] + RHS2[3] * RHS1[0] * RHS1[1];\n}\n"

If we want to compile this, we do expression=Val{false}:

f = build_function(du, u, p, t, target=Symbolics.CTarget(), expression=Val{false})
RuntimeGeneratedFunction(#=in Symbolics=#, #=using Symbolics=#, :((du, u, p, t)->begin
           #= /home/runner/work/Symbolics.jl/Symbolics.jl/src/build_function.jl:805 =#
-          ccall(("diffeqf", "/tmp/jl_eVUP6PInC1"), Cvoid, (Ptr{Float64}, Ptr{Float64}, Ptr{Float64}, Float64), du, u, p, t)
+          ccall(("diffeqf", "/tmp/jl_EPyXAXcpGN"), Cvoid, (Ptr{Float64}, Ptr{Float64}, Ptr{Float64}, Float64), du, u, p, t)
       end))

now we check it computes the same thing:

du = rand(2); du2 = rand(2)
 u = rand(2)
 p = rand(4)
 t = rand()
 f(du, u, p, t)
 lotka_volterra!(du2, u, p, t)
-du == du2 # true!
true
+du == du2 # true!
true
diff --git a/dev/tutorials/perturbation/index.html b/dev/tutorials/perturbation/index.html index 9c282424c..46ed1f6e5 100644 --- a/dev/tutorials/perturbation/index.html +++ b/dev/tutorials/perturbation/index.html @@ -112,4 +112,4 @@ \]

This looks very different from our first series E_pert. If they are the same, we should get $0$ if we subtract and expand both as multivariate Taylor series in $(e,M)$. Indeed:

taylor(taylor(E_pert′ - E_pert, e, 0:4), M, 0:4)

\[ \begin{equation} 0 \end{equation} - \]

+ \]