From b16bef1b572310b7977b3007e4c9513d0be0981c Mon Sep 17 00:00:00 2001 From: jverzani Date: Wed, 30 Aug 2023 17:51:23 -0400 Subject: [PATCH 1/2] code cleanup --- Project.toml | 2 +- docs/src/introduction.md | 56 ++++++++--- ext/SymPyPythonCallSymbolicsExt.jl | 13 ++- src/SymPyPythonCall.jl | 4 - src/assumptions.jl | 2 +- src/equations.jl | 1 + src/gen_methods.jl | 151 ++++++++++++++++------------- src/lambdify.jl | 10 +- src/types.jl | 6 +- src/utils.jl | 1 + 10 files changed, 148 insertions(+), 98 deletions(-) diff --git a/Project.toml b/Project.toml index 98aad175..00f8b662 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "SymPyPythonCall" uuid = "bc8888f7-b21e-4b7c-a06a-5d9c9496438c" authors = ["jverzani and contributors"] -version = "0.1.1" +version = "0.1.2" [deps] CommonEq = "3709ef60-1bee-4518-9f2f-acd86f176c50" diff --git a/docs/src/introduction.md b/docs/src/introduction.md index 41e05579..d667bf86 100644 --- a/docs/src/introduction.md +++ b/docs/src/introduction.md @@ -2,17 +2,21 @@ !!! note "SymPyPythonCall" - This is a test of what works in `SymPyPythonCall`. - `SymPyPythonCall` is just a temporary package for now, with the expectation that it may become `SymPy`. - There would be a few deprecations: - * `@vars` would be deprecated; use `@syms` only + `SymPyPythonCall.jl` is like the `SymPy.jl` package in that it allows access to the underlying `SymPy` + library for `Python` from within `Julia`. + + Whereas `SymPy.jl` uses the `PyCall` package, `SymPyPythonCall` uses the `PythonCall` package to provide + the bridge between `Julia` and `Python`. + + Though the two packages work similarly, there are a few differences: + + * `@vars` of `SymPy` is not provided; use the more powerful `@syms` macro only * `Base.show` isn't *currently* using pretty printing - * `elements` for sets would be removed (convert to a `Set` by default) - * Would `Q` be exported? (only the slant italic Q is currently) - * What to do with matrices? E.g. `A\b` is failing now + * Sets are handled differently, with finite sets converted to `Julia` `Set`s. + * Currently `Q` is not exported. (only the slant italic Q is currently) + * Matrix support may not be complete. * `sympy.poly` *not* `sympy.Poly` - * would we export `CommonEq`? - * `limit(ex, x, c)` deprecated; use `limit(ex, x=>c)` or `sympy.limit` + * The form `limit(ex, x, c)` is not provided; use `limit(ex, x=>c)` or `sympy.limit` * no new special functions exported, just the ones in SpecialFunctions.jl This document provides an introduction to using `SymPy` within `Julia`. @@ -218,9 +222,12 @@ z + 1 + pi !!! note - The calling pattern for `subs` is different from a typical `Julia` function call. The `subs` call is `object.method(arguments)` whereas a more "`Julia`n" function call is `method(objects, other objects....)`, as `Julia` offers multiple dispatch of methods. `SymPy` uses the Python calling method, adding in `Julia`n style when appropriate for generic usage within `Julia`. `SymPyPythonCall` imports many generic functions from the underlying `sympy` module and specializes them on a symbolic first argument. + The calling pattern for `subs` is different from a typical `Julia` function call. The `subs` call is `object.method(arguments...)` whereas a more "`Julia`n" function call is `method(objects, arguments...)`, as `Julia` offers multiple dispatch of methods. `SymPyPythonCall` uses the Python calling method, adding in `Julia`n style when appropriate for generic usage within `Julia`. `SymPyPythonCall` imports many generic functions from the underlying `sympy` module and specializes them on a symbolic first argument. + +!!! note + The SymPy documentation for many functions can be read from the terminal using `Base.Docs.getdoc(ex)`, as in `Base.Docs.getdoc(sin(x))`. - For `subs`, the simple substitution `ex.object(x,a)` is similar to simple function evaluation, so `Julia`'s call notation will work. To specify the pairing off of `x` and `a`, the `=>` pairs notation is used. +For `subs`, the simple substitution `ex.object(x,a)` is similar to simple function evaluation, so `Julia`'s call notation will work. To specify the pairing off of `x` and `a`, the `=>` pairs notation is used. This calling style will be equivalent to the last: @@ -610,7 +617,7 @@ julia> q.coeffs() !!! note - This is `sympy` not `SymPyPythonCall`. Using `sympy.Poly` is not supported. The `Poly` constructor from SymPy is *not* a function, so is not exported when `SymPyCa;;` is loaded. + This is `sympy` not `SymPyPythonCall` qualifying the method call. Using `sympy.Poly` is not supported. The `Poly` constructor from SymPy is *not* a function, so is not exported when `SymPyCall` is loaded. ## Polynomial roots: solve, real_roots, polyroots, nroots @@ -841,6 +848,31 @@ julia> solve(p) Dict(a => (-b*x - c)/x^2) ``` +The output of `solveset` in Python is always a set, which may be finite or not. Finite sets are converted to `Set`s in `Julia`. Infinite sets have no natural counterpart and are not realized. Rather, they can be queried, as with `contains(haystack, needle)`. For example: + +```jldoctest introduction +julia> u = solveset(sin(x) ≧ 0) # [\geqq] or with u = solveset(Ge(sin(x), 0)) +ConditionSet(x, sin(x) >= 0, Complexes) + +julia> contains(u, PI/2) +true + +julia> contains(u, 3PI/2) +false +``` + +Infinite sets can have unions and intersections taken, but the SymPy methods must be called: + +```jldoctest introduction +julia> v = solveset(cos(x) ≧ 0) +ConditionSet(x, cos(x) >= 0, Complexes) + +julia> contains.((u, v, u.intersect(v), u.union(v)), 3PI/4) +(true, false, false, true) +``` + +--- + Systems of equations can be solved as well. We specify them within a vector of expressions, `[ex1, ex2, ..., exn]` where a found solution is one where all the expressions are 0. For example, to solve this diff --git a/ext/SymPyPythonCallSymbolicsExt.jl b/ext/SymPyPythonCallSymbolicsExt.jl index 7f5076fb..79743383 100644 --- a/ext/SymPyPythonCallSymbolicsExt.jl +++ b/ext/SymPyPythonCallSymbolicsExt.jl @@ -97,7 +97,7 @@ function __init__() PythonCall.pyconvert_add_rule("sympy.core.relational:Equality", Symbolics.Equation, pyconvert_rule_sympy_equality) # core numbers - add_pyconvert_rule(f, cls) = PythonCall.pyconvert_add_rule(cls, Symbolics.Num, f) + add_pyconvert_rule(f, cls, T=Symbolics.Num) = PythonCall.pyconvert_add_rule(cls, T, f) add_pyconvert_rule("sympy.core.numbers:Pi") do T::Type{Symbolics.Num}, x PythonCall.pyconvert_return(Symbolics.Num(pi)) @@ -108,14 +108,13 @@ function __init__() add_pyconvert_rule("sympy.core.numbers:Infinity") do T::Type{Symbolics.Num}, x PythonCall.pyconvert_return(Symbolics.Num(Inf)) end - #= complex numbers and Num needs some workaround - add_pyconvert_rule("sympy.core.numbers:ImaginaryUnit") do T::Type{Symbolics.Num}, x - PythonCall.pyconvert_return(Symbolics.Num(im)) + # Complex{Num} + add_pyconvert_rule("sympy.core.numbers:ImaginaryUnit", Complex{Symbolics.Num}) do T::Type{Complex{Symbolics.Num}}, x + PythonCall.pyconvert_return(Complex(Symbolics.Num(0), Symbolics.Num{1})) end - add_pyconvert_rule("sympy.core.numbers:ComplexInfinity") do T::Type{Symbolics.Num}, x - PythonCall.pyconvert_return(Symbolics.Num(Inf)) # errors: Complex(Inf,Inf))) + add_pyconvert_rule("sympy.core.numbers:ComplexInfinity", Complex{Symbolics.Num}) do T::Type{Complex{Symbolics.Num}}, x + PythonCall.pyconvert_return(Complex(Symbolics.Num(0), Symbolics.Num(Inf)) ) end - =# end end diff --git a/src/SymPyPythonCall.jl b/src/SymPyPythonCall.jl index a08377e6..854793eb 100644 --- a/src/SymPyPythonCall.jl +++ b/src/SymPyPythonCall.jl @@ -90,10 +90,6 @@ function __init__() PythonCall.pycopy!(__FALSE__, pyconvert(Py, false)) PythonCall.pycopy!(__𝑄__, __sympy__.Q) - end - - - end diff --git a/src/assumptions.jl b/src/assumptions.jl index ef2a84d9..080034e7 100644 --- a/src/assumptions.jl +++ b/src/assumptions.jl @@ -52,7 +52,7 @@ ask(x::Nothing, args...) = x # ## for now, we can combine terms logically with And, Or, Not... # ## these are in logic module - +# XXX This is from SymPy. Do we want this in SymPyPythonCall? # ## We make a module Q to hold the assumptions # ## this follows this page http://docs.sympy.org/0.7.5/_modules/sympy/assumptions/ask.html diff --git a/src/equations.jl b/src/equations.jl index 14bba83c..a4acd04b 100644 --- a/src/equations.jl +++ b/src/equations.jl @@ -1,3 +1,4 @@ +# Define ~ usage Base.:~(lhs::Number, rhs::Sym) = ~(promote(lhs, rhs)...) Base.:~(lhs::Sym, rhs::Number) = ~(promote(lhs, rhs)...) Base.:~(lhs::Sym, rhs::Sym) = asSymbolic(sympy.py.Eq(lhs.py, rhs.py)) diff --git a/src/gen_methods.jl b/src/gen_methods.jl index 6ddd8527..d514b600 100644 --- a/src/gen_methods.jl +++ b/src/gen_methods.jl @@ -1,36 +1,48 @@ -## XXX We should be hooking to the pyconvert methods XXX -## we need Py -> Sym (asSymbolic) -## we need Sym -> Py unSym +# we have this picture for calling SymPy methods +# Julia ⋯⋯⋯⋯ > Julia +# | ^ +# | | +# | (unSym, Py, ↓) | (asSymbolic, ↑) +# | | +# v (SymPy) | +# Python ------> Python + + +""" + asSymbolic(x) + +Convert `Python` object into symbolic `Julia` object. Aliased to `↑`. +""" +asSymbolic(x) = Sym(pyconvert(Py, x)) asSymbolic(x::Sym) = x -function asSymbolic(x::Py) - - cls = x.__class__ - - if pyconvert(Bool, cls == pybuiltins.list) - return asSymbolic.(x) - elseif pyconvert(Bool, cls == pybuiltins.dict) - return Dict(asSymbolic(k) => asSymbolic(v) for (k,v) ∈ pyconvert(PyDict, x)) - elseif pyconvert(Bool, cls == pybuiltins.tuple) - return Tuple(asSymbolic(xᵢ) for xᵢ ∈ x) - elseif pyconvert(Bool, cls == pybuiltins.set) - return Set(asSymbolic(xᵢ) for xᵢ ∈ x) - # elseif pyconvert(Bool, cls == pybuiltins.list) - end +asSymbolic(x::String) = Sym(x) +PyTypeName(x) = Val(Symbol(PythonCall.pyconvert_typename(pytype(x)))) +asSymbolic(x::Py) = asSymbolic(PyTypeName(x), x) # special case based on typename - Bool(pygetattr(x, "is_FiniteSet", false)) && return Set(asSymbolic(xᵢ) for xᵢ ∈ x) +asSymbolic(::Val{T}, x::Py) where {T} = Sym(x) # fallback - if Bool(pygetattr(x, "is_Matrix", false)) - sz = pyconvert(Tuple, x.shape) - !isa(sz[1], Real) && return Sym(x) # MatrixSymbol special case - return [asSymbolic(x.__getitem__((i-1, j-1))) for i ∈ 1:sz[1], j ∈ 1:sz[2]] - end - Sym(x) +asSymbolic(::Val{Symbol("builtins:list")}, x::Py) = isempty(x) ? Sym[] : [asSymbolic(xᵢ) for xᵢ ∈ x] # XXX are lists Tuples or vectors??? +asSymbolic(::Val{Symbol("builtins:tuple")}, x::Py) = Tuple(asSymbolic(xᵢ) for xᵢ ∈ x) +asSymbolic(::Val{Symbol("builtins:dict")}, x::Py) = Dict(asSymbolic(k) => asSymbolic(v) for (k,v) ∈ pyconvert(PyDict, x)) +asSymbolic(::Val{Symbol("builtins:set")}, x::Py) = Set(asSymbolic(xᵢ) for xᵢ ∈ x) +asSymbolic(::Val{Symbol("sympy.sets.sets:FiniteSet")}, x::Py) = Set(asSymbolic(xᵢ) for xᵢ ∈ x) +asSymbolic(::Val{Symbol("sympy.core.containers:Tuple")}, x::Py) = Tuple(asSymbolic(xᵢ) for xᵢ ∈ x) + +function _as_symbolic_dense_matrix(x) + sz = pyconvert(Tuple, x.shape) + return [asSymbolic(x.__getitem__((i-1, j-1))) for i ∈ 1:sz[1], j ∈ 1:sz[2]] end -asSymbolic(x::String) = x -asSymbolic(x) = Sym(pyconvert(Py, x)) -↑(args...; kwargs...) = asSymbolic(args...; kwargs...) +asSymbolic(::Val{Symbol("sympy.matrices.dense:MutableDenseMatrix")}, x::Py) = _as_symbolic_dense_matrix(x) +asSymbolic(::Val{Symbol("sympy.matrices.dense:ImmutableDenseMatrix")}, x::Py) = _as_symbolic_dense_matrix(x) +function _as_symbolic_symbolic_matrix(x) + sz = pyconvert(Tuple, x.shape) + !isa(sz[1], Real) && return Sym(x) # MatrixSymbol special case + return [asSymbolic(x.__getitem__((i-1, j-1))) for i ∈ 1:sz[1], j ∈ 1:sz[2]] +end +asSymbolic(::Val{Symbol("sympy.matrices.expressions.matexpr:MatrixSymbol")}, x::Py) = _as_symbolic_symbolic_matrix(x) +asSymbolic(::Val{Symbol("sympy.matrices.expressions.inverse:Inverse")}, x::Py) = _as_symbolic_symbolic_matrix(x) # used to pass arguments down into calls unSym(x) = x @@ -42,7 +54,7 @@ unSym(x::Dict) = convert(PyDict,Dict(unSym(k) => unSym(v) for (k,v) ∈ x)) # Ab unSym(x::Set) = sympy.py.Set(unSym(collect(x))) unSym(x::Irrational{:π}) = unSym(sympy.pi) unSymkwargs(kw) = (k=>unSym(v) for (k,v) ∈ kw) -↓(args...; kwargs...) = unSym(args...; kwargs...) + #PythonCall.Py(x::Sym) = ↓(x) PythonCall.Py(x::NTuple{N,T}) where {N, T <: SymbolicObject} = ↓(x) @@ -50,6 +62,9 @@ PythonCall.Py(x::Vector{T}) where {T <: SymbolicObject} = unSym.(x) PythonCall.Py(x::Matrix{T}) where {T <: SymbolicObject} = unSym.(x) PythonCall.Py(x::Set{T}) where {T <: SymbolicObject} = unSym(x) +# use ↑, ↓ shortcuts +↑(args...; kwargs...) = asSymbolic(args...; kwargs...) +↓(args...; kwargs...) = unSym(args...; kwargs...) ## -------------------------------------------------- @@ -98,7 +113,7 @@ generic_methods = ( (:sympy, :Max, :Base, :max), (:sympy, :Abs, :Base, :abs), (:sympy, :Min, :Base, :min), - + # (:sympy, :re, :Base, :real), (:sympy, :im, :Base, :imag), @@ -116,28 +131,29 @@ generic_methods = ( # diff (:sympy, :diff, :CommonEq, :diff), + # collect (:sympy, :collect, :Base, :collect), # SpecialFunctions - (:sympy, :airyai , :SpecialFunctions, :airyai), + (:sympy, :airyai , :SpecialFunctions, :airyai), (:sympy, :airyaiprime , :SpecialFunctions, :airyaiprime), - (:sympy, :airybi , :SpecialFunctions, :airybi), - (:sympy, :besseli , :SpecialFunctions, :besseli), - (:sympy, :besselj , :SpecialFunctions, :besselj), - (:sympy, :besselk , :SpecialFunctions, :besselk), - (:sympy, :bessely , :SpecialFunctions, :bessely), - (:sympy, :beta , :SpecialFunctions, :beta), - (:sympy, :erf , :SpecialFunctions, :erf), - (:sympy, :erfc , :SpecialFunctions, :erfc), - (:sympy, :erfi , :SpecialFunctions, :erfi), - (:sympy, :erfinv , :SpecialFunctions, :erfinv), - (:sympy, :erfcinv , :SpecialFunctions, :erfcinv), - (:sympy, :gamma , :SpecialFunctions, :gamma), - (:sympy, :digamma , :SpecialFunctions, :digamma), - (:sympy, :polygamma , :SpecialFunctions, :polygamma), - (:sympy, :hankel1, :SpecialFunctions, :hankelh1), - (:sympy, :hankel2, :SpecialFunctions, :hankelh2), - (:sympy, :zeta , :SpecialFunctions, :zeta), + (:sympy, :airybi , :SpecialFunctions, :airybi), + (:sympy, :besseli , :SpecialFunctions, :besseli), + (:sympy, :besselj , :SpecialFunctions, :besselj), + (:sympy, :besselk , :SpecialFunctions, :besselk), + (:sympy, :bessely , :SpecialFunctions, :bessely), + (:sympy, :beta , :SpecialFunctions, :beta), + (:sympy, :erf , :SpecialFunctions, :erf), + (:sympy, :erfc , :SpecialFunctions, :erfc), + (:sympy, :erfi , :SpecialFunctions, :erfi), + (:sympy, :erfinv , :SpecialFunctions, :erfinv), + (:sympy, :erfcinv , :SpecialFunctions, :erfcinv), + (:sympy, :gamma , :SpecialFunctions, :gamma), + (:sympy, :digamma , :SpecialFunctions, :digamma), + (:sympy, :polygamma , :SpecialFunctions, :polygamma), + (:sympy, :hankel1, :SpecialFunctions, :hankelh1), + (:sympy, :hankel2, :SpecialFunctions, :hankelh2), + (:sympy, :zeta , :SpecialFunctions, :zeta), ) # comment, Py(...) speeds things up a bit. @@ -152,27 +168,30 @@ end # pmod, pmeth, meth #const new_exported_methods = ( - (:sympy, :simplify, :simplify), + (:sympy, :simplify, :simplify), (:sympy, :expand_trig, :expand_trig), - (:sympy, :expand, :expand), - (:sympy, :together, :together), - (:sympy, :apart, :apart), - (:sympy, :factor, :factor), - (:sympy, :cancel, :cancel), - (:sympy, :degree, :degree), - (:sympy, :integrate, :integrate), - (:sympy, :real_roots, :real_roots), - (:sympy, :roots, :roots), - (:sympy, :nroots, :nroots), - (:sympy, :dsolve, :dsolve), - (:sympy, :nsolve, :nsolve), - (:sympy, :linsolve, :linsolve), + (:sympy, :expand, :expand), + (:sympy, :together, :together), + (:sympy, :apart, :apart), + (:sympy, :factor, :factor), + (:sympy, :cancel, :cancel), + # + (:sympy, :degree, :degree), + # + (:sympy, :integrate, :integrate), + # + (:sympy, :real_roots, :real_roots), + (:sympy, :roots, :roots), + (:sympy, :nroots, :nroots), + (:sympy, :dsolve, :dsolve), + (:sympy, :nsolve, :nsolve), + (:sympy, :linsolve, :linsolve), (:sympy, :nonlinsolve, :nonlinsolve), - (:sympy, :solveset, :solveset), - - (:sympy, :series, :series), - (:sympy, :summation, :summation), - (:sympy, :hessian, :hessian), + (:sympy, :solveset, :solveset), + # + (:sympy, :series, :series), + (:sympy, :summation, :summation), + (:sympy, :hessian, :hessian), ) for (pmod, pmeth, jmeth) ∈ new_exported_methods diff --git a/src/lambdify.jl b/src/lambdify.jl index 2d8207ed..badb4c52 100644 --- a/src/lambdify.jl +++ b/src/lambdify.jl @@ -45,7 +45,7 @@ __HEAVISIDE__ = (a...) -> (a[1] < 0 ? 0 : (a[1] > 0 ? 1 : (length(a) > 1 ? a[2] fn_map = Dict( "Add" => :+, "Sub" => :-, - "Mul" => :*, # :(SymPy.__PROD__) + "Mul" => :((as...)->prod(as)), #:*, # :(SymPy.__PROD__) "Div" => :/, "Pow" => :^, "re" => :real, @@ -77,12 +77,9 @@ map_fn(key, fn_map) = haskey(fn_map, key) ? fn_map[key] : Symbol(key) Base.convert(::Type{Expr}, x::SymbolicObject) = walk_expression(x) function walk_expression(ex; values=Dict(), fns=Dict()) - fns_map = merge(fn_map, fns) vals_map = merge(val_map, values) - - fn = Introspection.funcname(ex) - + fn = Introspection.funcname(first(ex)) # special case `F(t) = ...` output from ODE # this may be removed if it proves a bad idea.... if fn == "Equality" && Bool(lhs(ex).is_Function) @@ -107,6 +104,8 @@ function walk_expression(ex; values=Dict(), fns=Dict()) return walk_expression.(Introspection.args(ex), values=values, fns=fns) elseif fn == "Indexed" return Expr(:ref, [walk_expression(a, values=values, fns=fns) for a in Introspection.args(ex)]...) + elseif fn == "Order" + return 0 elseif haskey(vals_map, fn) return vals_map[fn] end @@ -212,7 +211,6 @@ function lambdify(ex::Sym, vars=free_symbols(ex); fns=Dict(), values=Dict(), use_julia_code=false, invoke_latest=true) - body = convert_expr(ex, fns=fns, values=values, use_julia_code=use_julia_code) ex = expr_to_function(body, vars) if invoke_latest diff --git a/src/types.jl b/src/types.jl index 4cb1fed8..bc02d442 100644 --- a/src/types.jl +++ b/src/types.jl @@ -17,6 +17,7 @@ end # allow dot access to underlying methods, properties function Base.getproperty(x::Sym, a::Symbol) + a == :py && return getfield(x,a) py = Py(x) @@ -24,6 +25,7 @@ function Base.getproperty(x::Sym, a::Symbol) val === nothing && return nothing # hacky, but seems necessary for sympy.Poly, sympy.Curve, and others + contains(string(pytype(val)), "ManagedProperties") && @info "Managed Properties is deprecated in Sympy; can you report:", py, a contains(string(pytype(val)), "ManagedProperties") && return (args...;kwargs...) -> ↑(val(unSym.(args)...; unSymkwargs(kwargs)...)) meth = pygetattr(val, "__call__", nothing) @@ -41,7 +43,8 @@ Sym(x::Symbol) = Sym(string(x)) Sym(x::Sym) = x Sym(x::Complex{Bool}) = 1*real(x) + 1*imag(x)*IM Sym(x::Complex) = Sym(real(x)) + Sym(imag(x))*IM -Sym(x::Number) = sympy.sympify(x) #Sym(sympy.pyconvert(Py, x)) +Sym(x::Integer) = sympy.core.numbers.Integer(x) +Sym(x::Number) = Sym(pyconvert(Py, x)) # sympy.sympify(x) #Sym(sympy.pyconvert(Py, x)) Sym(x::PyArray) = Sym.(x) Sym(x::PyDict) = Dict(Sym(k) => Sym(v) for (k,v) ∈ x) Sym(x::PyIterable) = (Sym(u) for u ∈ x) @@ -58,6 +61,7 @@ Sym(x::Irrational{:e}) = sympy.E struct SymbolicCallable val end +Base.show(io::IO, λ::SymbolicCallable) = print(io, "Callable SymPy method") function (v::SymbolicCallable)(args...; kwargs...) ↑(v.val(unSym.(args)...; unSymkwargs(kwargs)...)) end diff --git a/src/utils.jl b/src/utils.jl index 3100aec7..171869f8 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1,6 +1,7 @@ ## -------------------------------------------------- ## no Doc trick Base.Docs.getdoc(u::Sym) = Base.Docs.getdoc(Py(u)) + ## -------------------------------------------------- is_(k::Symbol, x::Sym) = is_(k, Py(x)) From ed80a8d08540c5824033bb2347c48289f46a3465 Mon Sep 17 00:00:00 2001 From: jverzani Date: Wed, 30 Aug 2023 18:06:51 -0400 Subject: [PATCH 2/2] doctest fix=true --- docs/src/introduction.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/src/introduction.md b/docs/src/introduction.md index d667bf86..94be6c49 100644 --- a/docs/src/introduction.md +++ b/docs/src/introduction.md @@ -123,13 +123,13 @@ We jump ahead for a second to illustrate, but here we see that `solve` will resp ```jldoctest introduction julia> solve(x^2 + 1) # ±i are not real -Any[] +Sym[] ``` ```jldoctest introduction julia> solve(y1 + 1) # -1 is not positive -Any[] +Sym[] ``` @@ -140,7 +140,7 @@ julia> @syms u1::positive u2::positive (u1, u2) julia> solve(u1 + u2) # empty, though solving u1 - u2 is not. -Any[] +Sym[] ``` Additionally you can rename arguments using pair notation: @@ -173,8 +173,8 @@ function, the second to `SymPy`'s: ```jldoctest introduction julia> [asin(1), asin(Sym(1))] 2-element Vector{Sym}: - 1.57079632679490 - pi/2 + 1.5707963267948966 + pi/2 ```