Skip to content

extend substitute syntax to more than one pair; add call method conve… - #11

Closed
jverzani wants to merge 2 commits into
JuliaGiac:mainfrom
jverzani:substitute
Closed

extend substitute syntax to more than one pair; add call method conve…#11
jverzani wants to merge 2 commits into
JuliaGiac:mainfrom
jverzani:substitute

Conversation

@jverzani

Copy link
Copy Markdown
Contributor

Extends the substitute syntax to multiple pairs of Pairs

Adds call syntax for symbolic expression when called with Pairs.

Again, I didn't include any tests, but can if you want.

@s-celles

s-celles commented Apr 30, 2026

Copy link
Copy Markdown
Collaborator

Maybe we should simply add doctests for all of this.
This is "lighter" than testset from Test.jl and provide both doc and test

Related #3

Comment thread src/types.jl Outdated
Uses call syntax for `substitute` when arguments are pairs.

# Examples
```julia

@s-celles s-celles Apr 30, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doctest such as
4785235#diff-5223be791c850df6b50e8950a30d18af4e58e429c96f51a52a9a743165a4f91dR21-R23
could probably be used here as it both documents code and test it

Comment thread src/substitute.jl
expr = substitute(expr, p)
end
expr
end

@s-celles s-celles Apr 30, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should ensure that

substitute(expr, x => y, y => x) 

give similar result than

substitute(expr, Dict(x => y, y => x))

See

julia> expr = x + 2*y
GiacExpr: x+2*y

julia> Giac.substitute(expr, Dict(x => y, y => x))
GiacExpr: y+2*x

julia> function substitute(expr, pairs...)
for p in pairs
expr = substitute(expr, p)
end
expr
end
substitute (generic function with 2 methods)

julia> substitute(expr, x => y, y => x)
Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable.
ERROR: StackOverflowError:
Stacktrace:
[1] substitute(expr::GiacExpr, pairs::Pair{GiacExpr, GiacExpr})
@ Main ./REPL[2]:1
[2] substitute(expr::GiacExpr, pairs::Pair{GiacExpr, GiacExpr}) (repeats 79983 times)
@ Main ./REPL[2]:3

We should also take care of order as Julia Dict don't preserve insertion order.

on the other side I don't think such method prototype exists in Symbolics.jl...

julia> using Symbolics

julia> methods(Symbolics.substitute)
# 1 method for generic function "substitute" from SymbolicUtils:
[1] substitute(expr, dict; fold, filterer)
@ ~/.julia/packages/SymbolicUtils/uHteK/src/substitute.jl:261

help?> Symbolics.substitute
substitute(expr, dict; fold=Val(false))

substitute any subexpression that matches a key in dict with the corresponding value. If fold=Val(false), expressions which can be evaluated won't be evaluated.

julia> substitute(1+sqrt(y), Dict(y => 2), fold=Val(true))
2.414213562373095
julia> substitute(1+sqrt(y), Dict(y => 2), fold=Val(false))
1 + sqrt(2)

─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

substitute(expr, s; fold=Val(false))

Performs the substitution on expr according to rule(s) s. If fold=Val(true), expressions which can be fully evaluated will be evaluated to a number.

│ Does not penetrate `Differential`
│
│  As of Symbolics.jl v7 (SymbolicUtils.jl v4), substitute does not recurse into the arguments of Differential expressions. For example, substitute(D(x), Dict(x => y)) returns D(x), not D(y). Use
│  substitute_in_deriv or substitute_in_deriv_and_depvar to substitute inside Differential applications.

Examples
≡≡≡≡≡≡≡≡

julia> @variables t x y z(t)
4-element Vector{Num}:
t
x
y
z(t)
julia> ex = x + y + sin(z)
(x + y) + sin(z(t))
julia> substitute(ex, Dict([x => z, sin(z) => z^2]))
(z(t) + y) + (z(t) ^ 2)
julia> substitute(sqrt(2x), Dict([x => 1]))
sqrt(2)
julia> substitute(sqrt(2x), Dict([x => 1]); fold=Val(true))
1.4142135623730951

I even wonder if all this string based version of substitute shouldn't be written using

 Giac.Commands.subst(expr, [x, y], [y, x])

s-celles added a commit that referenced this pull request May 1, 2026
Refactor substitute(expr, dict) and substitute(matrix, dict) to call
GiacCxxBindings.giac_subst directly with structured Gen vectors built
via make_vect, instead of formatting a subst(...) command string and
re-parsing it through GIAC. Simultaneous-substitution semantics are
preserved (a single giac_subst call with [vars] / [vals] list args, the
same path the parser takes for bracketed-list literals), and Float64
replacement values are no longer at risk from textual round-trips. On a
representative non-trivial expression with two pairs over 1000 calls,
this is roughly 1.5-2x faster than the prior implementation.

Also add two ergonomic surfaces aligned with Symbolics.substitute:

  substitute(expr, x => 1, y => 2)         # varargs of pairs
  substitute(matrix, x => 1, y => 2)       # matrix counterpart
  expr(a => 15, b => 10, c => 5, d => 0)   # call-syntax sugar

Both delegate to substitute(expr, Dict(pairs)) so they inherit the
simultaneous semantics. The call-syntax overload requires at least one
pair so existing function-evaluation calls (u(0), f(x)) keep their
meaning. Call-syntax idea contributed by @jverzani in PR #11.

Tests: add explicit coverage for the canonical swap, value-references-
key, GiacMatrix analogs, varargs/dict equivalence, zero-pairs no-op,
Float64 precision preservation, and call-syntax (single pair, multi-
pair == substitute, swap, u(0) non-regression). Total +47 passing
tests, suite green at 8045 pass.

Bump 0.11.2 -> 0.12.0 (MINOR per SemVer for the new methods).
Internal helper _build_subst_command removed (no callers remain).
CHANGELOG entries for 0.11.1 and 0.11.2 backfilled from git tags.
@s-celles

s-celles commented May 1, 2026

Copy link
Copy Markdown
Collaborator

Closing this PR as I think all features have been implemented in #13
Thanks @jverzani for this idea of call method convention it should simplify usage.

@s-celles s-celles closed this May 1, 2026
@jverzani

jverzani commented May 1, 2026

Copy link
Copy Markdown
Contributor Author

Great. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants