Skip to content

Commit e543939

Browse files
author
Release Manager
committed
gh-40532: Implement compositional inverse fix #40286 ### 📝 Checklist <!-- Put an `x` in all the boxes that apply. --> - [ ] The title is concise and informative. - [ ] The description explains in detail what this PR is about. - [ ] I have linked a relevant issue or discussion. - [ ] I have created tests covering the changes. - [ ] I have updated the documentation and checked the documentation preview. ### ⌛ Dependencies <!-- List all open PRs that this PR logically depends on. For example, --> <!-- - #12345: short description why this is a dependency --> <!-- - #34567: ... --> URL: #40532 Reported by: user202729 Reviewer(s): Martin Rubey, Travis Scrimshaw, user202729
2 parents 6a8faff + 4d68334 commit e543939

File tree

2 files changed

+121
-0
lines changed

2 files changed

+121
-0
lines changed

src/sage/modules/free_module_element.pyx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4271,6 +4271,50 @@ cdef class FreeModuleElement(Vector): # abstract base class
42714271
return vector(ring, coeffs)
42724272
return vector(coeffs)
42734273
4274+
def compositional_inverse(self, allow_multivalued_inverse=True, **kwargs):
4275+
"""
4276+
Find the compositional inverse of this symbolic function.
4277+
4278+
INPUT: see :meth:`sage.symbolic.expression.Expression.compositional_inverse`
4279+
4280+
.. SEEALSO::
4281+
4282+
:meth:`sage.symbolic.expression.Expression.compositional_inverse`
4283+
4284+
EXAMPLES::
4285+
4286+
sage: f(x, y, z) = (y, z, x)
4287+
sage: f.compositional_inverse()
4288+
(x, y, z) |--> (z, x, y)
4289+
4290+
TESTS::
4291+
4292+
sage: f.change_ring(SR)
4293+
(y, z, x)
4294+
sage: f.change_ring(SR).compositional_inverse()
4295+
Traceback (most recent call last):
4296+
...
4297+
ValueError: base ring must be a symbolic expression ring
4298+
"""
4299+
from sage.rings.abc import CallableSymbolicExpressionRing
4300+
if not isinstance(self.base_ring(), CallableSymbolicExpressionRing):
4301+
raise ValueError("base ring must be a symbolic expression ring")
4302+
from sage.symbolic.ring import SR
4303+
tmp_vars = [SR.symbol() for _ in range(self.parent().dimension())]
4304+
input_vars = self.base_ring().args()
4305+
from sage.symbolic.relation import solve
4306+
l = solve([a == b for a, b in zip(self.change_ring(SR), tmp_vars)], input_vars, solution_dict=True, **kwargs)
4307+
if not l:
4308+
raise ValueError("cannot find an inverse")
4309+
if len(l) > 1 and not allow_multivalued_inverse:
4310+
raise ValueError("inverse is multivalued, pass allow_multivalued_inverse=True to bypass")
4311+
d = l[0]
4312+
subs_dict = dict(zip(tmp_vars, input_vars))
4313+
for x in input_vars:
4314+
if set(d[x].variables()) & set(input_vars):
4315+
raise ValueError("cannot find an inverse")
4316+
return self.parent()([d[x].subs(subs_dict) for x in input_vars])
4317+
42744318
42754319
# ############################################
42764320
# Generic dense element

src/sage/symbolic/expression.pyx

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13443,6 +13443,83 @@ cdef class Expression(Expression_abc):
1344313443
raise TypeError("this expression must be a relation")
1344413444
return self / x
1344513445

13446+
def compositional_inverse(self, allow_multivalued_inverse=True, **kwargs):
13447+
"""
13448+
Find the compositional inverse of this symbolic function.
13449+
13450+
INPUT:
13451+
13452+
- ``allow_multivalued_inverse`` -- (default: ``True``); see example below
13453+
- ``**kwargs`` -- additional keyword arguments passed to :func:`sage.symbolic.relation.solve`.
13454+
13455+
.. SEEALSO::
13456+
13457+
:meth:`sage.modules.free_module_element.FreeModuleElement.compositional_inverse`.
13458+
13459+
EXAMPLES::
13460+
13461+
sage: f(x) = x+1
13462+
sage: f.compositional_inverse()
13463+
x |--> x - 1
13464+
sage: var("y")
13465+
y
13466+
sage: f(x) = x+y
13467+
sage: f.compositional_inverse()
13468+
x |--> x - y
13469+
sage: f(x) = x^2
13470+
sage: f.compositional_inverse()
13471+
x |--> -sqrt(x)
13472+
13473+
When ``allow_multivalued_inverse=False``, there is some additional checking::
13474+
13475+
sage: f(x) = x^2
13476+
sage: f.compositional_inverse(allow_multivalued_inverse=False)
13477+
Traceback (most recent call last):
13478+
...
13479+
ValueError: inverse is multivalued, pass allow_multivalued_inverse=True to bypass
13480+
13481+
Nonetheless, the checking is not always foolproof (``x |--> log(x) + 2*pi*I`` is another possibility)::
13482+
13483+
sage: f(x) = exp(x)
13484+
sage: f.compositional_inverse(allow_multivalued_inverse=False)
13485+
x |--> log(x)
13486+
13487+
Sometimes passing ``kwargs`` is useful, for example ``algorithm`` can be used
13488+
when the default solver fails::
13489+
13490+
sage: f(x) = (2/3)^x
13491+
sage: f.compositional_inverse()
13492+
Traceback (most recent call last):
13493+
...
13494+
KeyError: x
13495+
sage: f.compositional_inverse(algorithm="giac") # needs sage.libs.giac
13496+
x |--> -log(x)/(log(3) - log(2))
13497+
13498+
TESTS::
13499+
13500+
sage: f(x) = x+exp(x)
13501+
sage: f.compositional_inverse()
13502+
Traceback (most recent call last):
13503+
...
13504+
ValueError: cannot find an inverse
13505+
sage: f(x) = 0
13506+
sage: f.compositional_inverse()
13507+
Traceback (most recent call last):
13508+
...
13509+
ValueError: cannot find an inverse
13510+
sage: f(x, y) = (x, x)
13511+
sage: f.compositional_inverse()
13512+
Traceback (most recent call last):
13513+
...
13514+
ValueError: cannot find an inverse
13515+
sage: (x+1).compositional_inverse()
13516+
Traceback (most recent call last):
13517+
...
13518+
ValueError: base ring must be a symbolic expression ring
13519+
"""
13520+
from sage.modules.free_module_element import vector
13521+
return vector([self]).compositional_inverse(allow_multivalued_inverse=allow_multivalued_inverse, **kwargs)[0]
13522+
1344613523
def implicit_derivative(self, Y, X, n=1):
1344713524
"""
1344813525
Return the `n`-th derivative of `Y` with respect to `X` given

0 commit comments

Comments
 (0)