Skip to content

Commit 2248ae9

Browse files
committed
Implement compositional_inverse for symbolic functions
1 parent 94fe540 commit 2248ae9

File tree

2 files changed

+92
-0
lines changed

2 files changed

+92
-0
lines changed

src/sage/modules/free_module_element.pyx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4271,6 +4271,45 @@ cdef class FreeModuleElement(Vector): # abstract base class
42714271
return vector(ring, coeffs)
42724272
return vector(coeffs)
42734273
4274+
def compositional_inverse(self, allow_inverse_multivalued=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+
4289+
TESTS::
4290+
4291+
sage: f.change_ring(SR)
4292+
sage: f.change_ring(SR).compositional_inverse()
4293+
"""
4294+
from sage.rings.abc import CallableSymbolicExpressionRing
4295+
if not isinstance(self.base_ring(), CallableSymbolicExpressionRing):
4296+
raise ValueError("base ring must be a symbolic expression ring")
4297+
from sage.symbolic.ring import SR
4298+
tmp_vars = [SR.symbol() for _ in range(self.parent().dimension())]
4299+
input_vars = self.base_ring().args()
4300+
from sage.symbolic.relation import solve
4301+
l = solve([a == b for a, b in zip(self.change_ring(SR), tmp_vars)], input_vars, solution_dict=True, **kwargs)
4302+
if not l:
4303+
raise ValueError("cannot find an inverse")
4304+
if len(l) > 1 and not allow_inverse_multivalued:
4305+
raise ValueError("inverse is multivalued, pass allow_inverse_multivalued=True to bypass")
4306+
d = l[0]
4307+
subs_dict = dict(zip(tmp_vars, input_vars))
4308+
for x in input_vars:
4309+
if set(d[x].variables()) & set(input_vars):
4310+
raise ValueError("cannot find an inverse")
4311+
return self.parent()([d[x].subs(subs_dict) for x in input_vars])
4312+
42744313
42754314
# ############################################
42764315
# Generic dense element

src/sage/symbolic/expression.pyx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13512,6 +13512,59 @@ cdef class Expression(Expression_abc):
1351213512
S = S.subs(di)
1351313513
return S
1351413514

13515+
def compositional_inverse(self, allow_inverse_multivalued=True, **kwargs):
13516+
"""
13517+
Find the compositional inverse of this symbolic function.
13518+
13519+
INPUT:
13520+
13521+
- ``allow_inverse_multivalued`` -- (default: ``True``); see example below
13522+
- ``**kwargs`` -- additional keyword arguments passed to :func:`sage.symbolic.relation.solve`.
13523+
13524+
.. SEEALSO::
13525+
13526+
:meth:`sage.modules.free_module_element.FreeModuleElement.compositional_inverse`.
13527+
13528+
EXAMPLES::
13529+
13530+
sage: f(x) = x+1
13531+
sage: f.compositional_inverse()
13532+
sage: var("y")
13533+
sage: f(x) = x+y
13534+
sage: f.compositional_inverse()
13535+
sage: f(x) = x^2
13536+
sage: f.compositional_inverse()
13537+
13538+
When ``allow_inverse_multivalued=False``, there is some additional checking::
13539+
13540+
sage: f(x) = x^2
13541+
sage: f.compositional_inverse(allow_inverse_multivalued=False)
13542+
13543+
Nonetheless, the checking is not always foolproof (``x |--> log(x) + 2*pi*I`` is another possibility)::
13544+
13545+
sage: f(x) = exp(x)
13546+
sage: f.compositional_inverse(allow_inverse_multivalued=False)
13547+
13548+
Sometimes passing ``kwargs`` is useful, for example ``algorithm`` can be used
13549+
when the default solver fails::
13550+
13551+
sage: f(x) = (2/3)^x
13552+
sage: f.compositional_inverse()
13553+
sage: f.compositional_inverse(algorithm="giac") # needs sage.libs.giac
13554+
13555+
TESTS::
13556+
13557+
sage: f(x) = x+exp(x)
13558+
sage: f.compositional_inverse()
13559+
sage: f(x) = 0
13560+
sage: f.compositional_inverse()
13561+
sage: f(x, y) = (x, x)
13562+
sage: f.compositional_inverse()
13563+
sage: (x+1).compositional_inverse()
13564+
"""
13565+
from sage.modules.free_module_element import vector
13566+
return vector([self]).compositional_inverse(allow_inverse_multivalued=allow_inverse_multivalued, **kwargs)[0]
13567+
1351513568

1351613569
cpdef _repr_Expression(x):
1351713570
r"""

0 commit comments

Comments
 (0)