Differential Equations
Giac.jl provides symbolic solving of ordinary differential equations (ODEs) using GIAC's desolve command. The package includes a Differential operator following SciML/ModelingToolkit conventions for expressing derivatives naturally, plus a deprecated D alias kept during the transition window.
The Differential Operator
Differential(var) is the canonical, SciML-style partial-differentiation operator. It captures a single variable and is applied as a callable. For a mono-variable function declared with @giac_var t u(t), Differential(t)(u) represents u'(t). Repeated application gives higher orders; mixed variables compose for partial derivatives. See Calculus for a multi-variable walkthrough.
using Giac
using Giac.Commands: desolve
@giac_var t u(t)
Dt = Differential(t)
Dt(u) # u'(t)
Dt(Dt(u)) # u''(t) (adjacent same-var steps collapse)Comparison with raw GIAC syntax
Differential form | Raw GIAC | Description |
|---|---|---|
Differential(t)(u) | diff(u(t), t) | First derivative |
Differential(t)(Differential(t)(u)) | diff(u(t), t, 2) | Second derivative |
Differential(t)(u)(0) ~ 1 | "u'(0)=1" | Initial condition for u'(0) |
Unicode identifiers
Function and variable names may use any Unicode letters that Julia and GIAC accept:
@giac_var 𝑧 ϕ(𝑧)
Dz = Differential(𝑧)
Dz(ϕ) # diff(ϕ(𝑧), 𝑧)
Dz(Dz(ϕ)) # diff(ϕ(𝑧), 𝑧, 2)The deprecated D alias
Earlier versions of Giac.jl exposed a D operator (e.g. D(u), D(u, 2), D(D(u))). D is still available during a one-release deprecation window, with each call site emitting a Base.depwarn pointing to the canonical replacement. D will be removed in the next published release. See docs/migration/d_to_differential.md for the full mapping table.
The one D call shape that no longer works even with a warning: D(f) for a multi-argument function like @giac_var x y f(x, y) now raises ArgumentError instead of silently differentiating against the first variable. Use Differential(x)(f) (or Differential(y)(f)) explicitly.
First-Order ODEs
Basic Example
Solve τu' + u = U₀ with initial condition u(0) = 1:
using Giac
using Giac.Commands: desolve
@giac_var t u(t) tau U0
Dt = Differential(t)
# Define ODE: τu' + u = U₀
ode = tau * Dt(u) + u ~ U0
# Initial condition: u(0) = 1
initial = u(0) ~ 1
# Solve
result = desolve([ode, initial], t, :u)
# Returns: U0+(-U0+1)*exp(-t/tau)RC Circuit Example
@giac_var t V(t) R C Vs
Dt = Differential(t)
# Capacitor voltage ODE: RC·V' + V = Vs
ode = R * C * Dt(V) + V ~ Vs
initial = V(0) ~ 0
result = desolve([ode, initial], t, :V)
# Returns: Vs*(1-exp(-t/(R*C)))Second-Order ODEs
Harmonic Oscillator
Solve u'' + u = 0 with u(0) = 1, u'(0) = 0:
using Giac
using Giac.Commands: desolve
@giac_var t u(t)
Dt = Differential(t)
# Define ODE
ode = Dt(Dt(u)) + u ~ 0
# Initial conditions
u0 = u(0) ~ 1 # u(0) = 1
du0 = Dt(u)(0) ~ 0 # u'(0) = 0
# Solve
result = desolve([ode, u0, du0], t, :u)
# Returns: cos(t)Damped Oscillator
Solve u'' + 2ζω₀u' + ω₀²u = 0:
@giac_var t u(t) zeta omega0
Dt = Differential(t)
ode = Dt(Dt(u)) + 2*zeta*omega0*Dt(u) + omega0^2*u ~ 0
result = desolve([ode, u(0) ~ 1, Dt(u)(0) ~ 0], t, :u)Third-Order ODEs
Solve y''' - y = 0 with y(0) = 1, y'(0) = 1, y''(0) = 1:
using Giac
using Giac.Commands: desolve
@giac_var t y(t)
Dt = Differential(t)
# Define ODE
ode = Dt(Dt(Dt(y))) - y ~ 0
# Initial conditions
y0 = y(0) ~ 1
dy0 = Dt(y)(0) ~ 1
d2y0 = Dt(Dt(y))(0) ~ 1
# Solve
result = desolve([ode, y0, dy0, d2y0], t, :y)
# Returns: exp(t)Using Differential in ODE Expressions
The Differential operator's result type supports arithmetic, making it natural to build ODE expressions:
@giac_var t u(t) a b c
Dt = Differential(t)
# Build complex ODE expressions
ode1 = Dt(Dt(u)) + a*Dt(u) + b*u ~ c
ode2 = Dt(Dt(u)) - 4*Dt(u) + 4*u ~ 0
# Combine with other GiacExpr
forcing = sin(t)
ode3 = Dt(Dt(u)) + u ~ forcingImportant Notes
Function Name as Symbol
When calling desolve, pass the function name as a Symbol (:u, :y) rather than the function expression (u, y):
# Correct
desolve([ode, u(0) ~ 1], t, :u)
# Incorrect - GIAC expects just the name, not u(t)
desolve([ode, u(0) ~ 1], t, u) # May not work as expectedInitial Conditions with Differential
The Differential(t)(u)(0) syntax creates an unevaluated derivative condition that GIAC interprets correctly:
Dt = Differential(t)
Dt(u)(0) ~ 1 # Creates "u'(0)=1" for GIAC
Dt(Dt(u))(0) ~ 0 # Creates "u''(0)=0" for GIACThis works because the underlying DerivativeExpr is mono-variable. Point-evaluation syntax for partial derivatives of multi-variable functions (e.g. PDE boundary conditions) is not supported in this release; see spec 068 Open Questions for the planned path.
Systems of ODEs
GIAC can solve systems of first-order ODEs:
@giac_var t x(t) y(t)
Dt = Differential(t)
# dx/dt = y, dy/dt = -x
sys = [Dt(x) ~ y, Dt(y) ~ -x]
initial = [x(0) ~ 1, y(0) ~ 0]
# Solve as a system (pass both variables)
result = desolve([sys..., initial...], t, [:x, :y])Physics Applications
Exponential Decay
Model radioactive decay: dN/dt = -λN
@giac_var t N(t) lambda N0
Dt = Differential(t)
# Decay equation
ode = Dt(N) + lambda * N ~ 0
initial = N(0) ~ N0
result = desolve([ode, initial], t, :N)
# Returns: N0*exp(-lambda*t)Population Growth
Exponential growth model: dP/dt = rP
@giac_var t P(t) r P0
Dt = Differential(t)
ode = Dt(P) - r * P ~ 0
initial = P(0) ~ P0
result = desolve([ode, initial], t, :P)
# Returns: P0*exp(r*t)Newton's Law of Cooling
Temperature change: dT/dt = -k(T - T_env)
@giac_var t T(t) k T_env T0
Dt = Differential(t)
ode = Dt(T) + k * (T - T_env) ~ 0
initial = T(0) ~ T0
result = desolve([ode, initial], t, :T)
# Returns exponential approach to T_envLimitations
- ODEs only: GIAC's
desolveis designed for ordinary differential equations. For PDEs, consider Symbolics.jl + MethodOfLines.jl or other specialized packages. - Symbolic solutions:
desolvefinds closed-form analytical solutions when possible. For numerical solutions of ODEs, use DifferentialEquations.jl.
API Reference
Giac.Differential — Type
Differential(var::GiacExpr)
Differential(var::GiacExpr, order::Int)Canonical partial-differentiation operator, SciML/ModelingToolkit-style.
Differential(var) captures a single differentiation variable; the resulting callable applies a first-order partial derivative to its operand. The two-argument form Differential(var, n) produces an operator that applies the n-th order partial derivative in one step — equivalent to applying Differential(var) n times — and matches Symbolics.jl's Differential(x, n) shape.
Two operand forms
- Function-form:
Differential(x)(f)for@giac_var x y f(x,y)returns aDerivativeExprrepresenting∂f/∂x. Compose by chaining:Differential(y)(Differential(x)(f))is∂²f/∂y∂x(see Notation below). The order-2 shapeDifferential(x, 2)(f)produces the same result asDifferential(x)(Differential(x)(f))(steps collapse). - Bare-expression:
Differential(x)(x^2 + y*x)returns a plainGiacExpr(already simplified by GIAC) —2*x + yin this case. The order-2 shapeDifferential(x, 2)(x^3)returns6*x(after simplification). Aligns with SymPy.jl'sdiff(expr, var)and Symbolics.jl'sDifferential.
Notation (right-to-left Leibniz)
Giac.jl uses the right-to-left Leibniz convention for ∂ⁿf/∂v₁…∂vₙ: the rightmost variable is applied first, the leftmost last — consistent with the operator product (∂/∂v₁)·…·(∂/∂vₙ) f. So Differential(y)(Differential(x)(f)) (apply Differential(x) first, then Differential(y)) is written ∂²f/∂y∂x, and pretty-printed as D: ∂²f/∂y∂x. By Schwarz/Clairaut the value is symmetric for sufficiently smooth f, but the notation, the Base.show output, and the steps field track the order of application.
Composition
Repeated application on the same variable collapses adjacent steps: Differential(x)(Differential(x)(f)) stores [("x", 2)] rather than [("x", 1), ("x", 1)]. Mixed variables compose freely: Differential(y)(Differential(x)(f)) stores [("x", 1), ("y", 1)] (innermost-first) and prints as diff(diff(f(x,y),x),y) for GIAC.
The operators ^, *, and ∘ are also supported for compact composition, matching Symbolics.jl: Differential(t)^2 === Differential(t, 2), Differential(y) * Differential(x) ≡ Differential(y) ∘ Differential(x), and Differential(t)^0 === identity.
Examples
using Giac
@giac_var x y t f(x, y) u(t)
Differential(x)(f) # ∂f/∂x
Differential(y)(Differential(x)(f)) # ∂²f/∂y∂x (x first, y next)
Differential(t)(u) # u'(t) (mono-variable case)
# Order-n shorthand (Symbolics.jl-style)
Differential(t, 2)(u) # u''(t) — same as Differential(t)(Differential(t)(u))
Differential(x, 3)(f) # ∂³f/∂x³
Differential(x)(x^2 + y*x) # 2*x + y (bare expression)
Differential(x, 2)(x^3) # 6*x (bare expression, second derivative)Name collision with Symbolics.jl
Symbolics.Differential exists too. When both packages are loaded, qualify: Giac.Differential(x)(f) vs Symbolics.Differential(x). The two are independent types — there is no implicit interop in this release. See docs/migration/d_to_differential.md for the recipe.
See also
D: Deprecated mono-variable operator.Dis being removed in the next published release; migrateD(u) → Differential(t)(u).DerivativeExpr: The function-form result type.
Giac.D — Function
D(expr::GiacExpr) -> DerivativeExpr
D(expr::GiacExpr, n::Int) -> DerivativeExpr
D(d::DerivativeExpr) -> DerivativeExprDerivative operator following SciML/ModelingToolkit conventions.
Creates a DerivativeExpr that can be:
- Used in ODEs:
D(D(u)) + u ~ 0(converts to diff notation) - Called for initial conditions:
D(u)(0) ~ 1(produces prime notation u'(0)=1)
Arguments
expr::GiacExpr: A function expression created with@giac_var u(t)n::Int: Optional derivative order (default: 1)d::DerivativeExpr: A derivative expression to differentiate further
Examples
using Giac
using Giac.Commands: desolve
@giac_var t u(t)
# First derivative
D(u) # Represents u'
# Second derivative (two ways)
D(D(u)) # Chain D operators
D(u, 2) # Specify order directly
# ODE with initial conditions
ode = D(D(u)) + u ~ 0 # u'' + u = 0
u0 = u(0) ~ 1 # u(0) = 1
du0 = D(u)(0) ~ 0 # u'(0) = 0
desolve([ode, u0, du0], t, u) # Returns: cos(t)
# Third order example
@giac_var t y(t)
ode = D(y, 3) - y ~ 0 # y''' - y = 0
desolve([ode, y(0) ~ 1, D(y)(0) ~ 1, D(y,2)(0) ~ 1], t, y)See also
DerivativeExpr: The derivative expression typedesolve: Solving differential equations (viaGiac.Commands)
Giac.DerivativeExpr — Type
DerivativeExprRepresents a derivative expression for use in ODE initial conditions.
This type enables the D operator syntax following SciML conventions:
D(u)represents the first derivative u'D(D(u))orD(u, 2)represents the second derivative u''D(u)(0)produces "u'(0)" for GIAC initial conditions
Fields
base_expr::GiacExpr: The original function expression (e.g., u(t))funcname::String: The function name (e.g., "u")varname::String: The differentiation variable (e.g., "t")order::Int: The derivative order (1 for first derivative, 2 for second, etc.)
Example
@giac_var t u(t)
# Create derivative expressions
du = D(u) # First derivative
d2u = D(D(u)) # Second derivative
d2u = D(u, 2) # Alternative syntax
# Use in initial conditions
D(u)(0) ~ 1 # u'(0) = 1
D(u, 2)(0) ~ 0 # u''(0) = 0
# Use in ODEs (converts to diff notation)
ode = D(D(u)) + u ~ 0 # Equivalent to diff(u,t,2) + u = 0See also
Giac.DerivativePoint — Type
DerivativePointRepresents a derivative evaluated at a specific point, for use in ODE initial conditions.
This type is created when calling a DerivativeExpr with arguments, e.g., D(u)(0). It delays evaluation until used with the ~ operator to create an equation, because GIAC interprets prime notation differently in isolation vs. within desolve.
Fields
funcname::String: The function name (e.g., "u")order::Int: The derivative orderpoint_args::Vector{String}: The point arguments as strings
Example
@giac_var t u(t)
dp = D(u)(0) # Returns DerivativePoint, not GiacExpr
eq = D(u)(0) ~ 1 # Creates equation: "u'(0)=1"Giac.DerivativeCondition — Type
DerivativeConditionRepresents an unevaluated derivative initial condition for ODEs.
This type holds the string representation of a derivative condition (e.g., "u'(0)=1") without evaluating it through GIAC. When passed to desolve in an array, it gets converted to its string form, which GIAC interprets correctly.
Example
@giac_var t u(t)
dc = D(u)(0) ~ 1 # Returns DerivativeCondition: "u'(0)=1"
# Pass to desolve - the string is used directly
desolve([D(D(u)) + u ~ 0, u(0) ~ 1, D(u)(0) ~ 0], t, u)Giac.expand_derivatives — Function
expand_derivatives(x) -> xForce evaluation of any lazy derivative wrapper, returning a plain GiacExpr.
For Differential(var)(f) where f is a declared user function (DerivativeExpr), expand_derivatives calls GIAC's diff and returns the resulting GiacExpr. For everything else (already a GiacExpr produced by Giac.jl's eager bare-expression branch, or any value not wrapped in DerivativeExpr), it is the identity — so code written in the Symbolics.jl style (expand_derivatives(Differential(x)(expr))) stays correct on Giac.jl without behavioral change.
Examples
@giac_var x y f(x, y)
expand_derivatives(x^2 + y) # identity → x^2+y
expand_derivatives(Differential(x)(x^2)) # identity → 2*x (already a GiacExpr)
expand_derivatives(Differential(x)(f)) # → diff(f(x,y),x) as a GiacExprSee also
Differential: The operator producing lazy or eager derivatives.DerivativeExpr: The lazy function-form derivative wrapper.