Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

a lazy solver for functional equations #37033

Closed

Conversation

mantepse
Copy link
Contributor

@mantepse mantepse commented Jan 8, 2024

We enable sage to solve systems of functional equations of lazy power series, generalizing the existing possibility of defining series recursively. For example:

sage: L.<z> = LazyPowerSeriesRing(QQ)
sage: A = L.undefined()
sage: B = L.undefined()
sage: FA = A^2 + B^2 - 2 - z*B
sage: FB = B^3 + 2*A^3 - 3 - z*(A + B)
sage: L.define_implicitly([(A, [1]), (B, [1])], [FA, FB])
sage: A
1 + 1/6*z - 7/72*z^2 - 125/1296*z^3 - 3289/31104*z^4 - 23591/186624*z^5 - 1048607/6718464*z^6 + O(z^7)
sage: B
1 + 1/3*z + 7/36*z^2 + 47/324*z^3 + 1903/15552*z^4 + 2959/23328*z^5 + 500663/3359232*z^6 + O(z^7)

Dependencies: #37451, #37687, #37736, #37766, #37761, #37916

tscrim and others added 30 commits October 10, 2023 12:21
… of cache may contain variable, add failing test
…s, use 'is' for equality when finding input streams in Stream_uninitialized
@mantepse
Copy link
Contributor Author

I have one other question, concerning equality of parents: should I be using "==" or "is"? For example, in stream.py, _subs_in_caches:

            for i0, i in enumerate(indices):
                c = s._cache[i]
                if self._coefficient_ring == self._base_ring:
                    if c.parent() == self._PF:
                        c = retract(subs(c, var, val))
                        if c.parent() is not self._base_ring:
                            good = m - i0 - 1
                elif c.parent() == self._U:
                    c = c.map_coefficients(lambda e: subs(e, var, val))
                    try:
                        c = c.map_coefficients(lambda e: retract(e), self._base_ring)
                    except TypeError:
                        good = m - i0 - 1
                s._cache[i] = c

I actually think that I did it backwards here. I understand that computations could in principle return an element with parent that is not identical to the one I'm expecting. Do we have a nice parent where == is frequently (or at least sometimes) different from is?

mantepse added 2 commits May 10, 2024 21:02
…tion of polynomial ring (which shouldn't be necessary in a better designed InfinitePolynomialRing)
vbraun pushed a commit to vbraun/sage that referenced this pull request May 11, 2024
sagemathgh-37916: structure/coerce actions
    
We modify `ModuleAction` to prefer coercion of the base ring over
creating an action.  More precisely, before this branch, we have

```
sage: G = PolynomialRing(QQ, "x")
sage: S = PolynomialRing(MatrixSpace(QQ, 2), "x")
sage: G.gen() * S.gen()
[x 0]
[0 x]*x
```
instead of
```
            [1 0]
            [0 1]*x^2
```
and
```
sage: G = PolynomialRing(QQ, "x")
sage: S = PolynomialRing(InfinitePolynomialRing(QQ, "a"), "x")
sage: G.gen() * S.an_element()
x*x
```
instead of
```
x^2
```
which is at least surprising.

In the end, this is needed to make sagemath#37033 work.

Authors: @tscrim and @mantepse.
    
URL: sagemath#37916
Reported by: Martin Rubey
Reviewer(s): Travis Scrimshaw
vbraun pushed a commit to vbraun/sage that referenced this pull request May 12, 2024
sagemathgh-37916: structure/coerce actions
    
We modify `ModuleAction` to prefer coercion of the base ring over
creating an action.  More precisely, before this branch, we have

```
sage: G = PolynomialRing(QQ, "x")
sage: S = PolynomialRing(MatrixSpace(QQ, 2), "x")
sage: G.gen() * S.gen()
[x 0]
[0 x]*x
```
instead of
```
            [1 0]
            [0 1]*x^2
```
and
```
sage: G = PolynomialRing(QQ, "x")
sage: S = PolynomialRing(InfinitePolynomialRing(QQ, "a"), "x")
sage: G.gen() * S.an_element()
x*x
```
instead of
```
x^2
```
which is at least surprising.

In the end, this is needed to make sagemath#37033 work.

Authors: @tscrim and @mantepse.
    
URL: sagemath#37916
Reported by: Martin Rubey
Reviewer(s): Travis Scrimshaw
vbraun pushed a commit to vbraun/sage that referenced this pull request May 12, 2024
sagemathgh-37916: structure/coerce actions
    
We modify `ModuleAction` to prefer coercion of the base ring over
creating an action.  More precisely, before this branch, we have

```
sage: G = PolynomialRing(QQ, "x")
sage: S = PolynomialRing(MatrixSpace(QQ, 2), "x")
sage: G.gen() * S.gen()
[x 0]
[0 x]*x
```
instead of
```
            [1 0]
            [0 1]*x^2
```
and
```
sage: G = PolynomialRing(QQ, "x")
sage: S = PolynomialRing(InfinitePolynomialRing(QQ, "a"), "x")
sage: G.gen() * S.an_element()
x*x
```
instead of
```
x^2
```
which is at least surprising.

In the end, this is needed to make sagemath#37033 work.

Authors: @tscrim and @mantepse.
    
URL: sagemath#37916
Reported by: Martin Rubey
Reviewer(s): Travis Scrimshaw
@tscrim
Copy link
Collaborator

tscrim commented May 12, 2024

Permutation (sub)groups are good examples, but I think their group algebras might not fall under this. Hom spaces between equal-but-not-identical parents also have the same behavior IIRC. I'm not sure how many rings fall under this though. I would probably just be consistent with using == and != for all of them in _subs_in_cache just to be safe, even if it is marginally slower. Running a check with an example to verify this doesn't noticeably change the computation time would be good to do.

@tscrim tscrim removed the v: large label May 12, 2024
@mantepse
Copy link
Contributor Author

mantepse commented May 12, 2024

OK, I can do that.

Meanwhile, I discovered another oddity. There is a bug in LazyModuleElement.change_ring:

    def change_ring(self, ring):
        r"""
        Return ``self`` with coefficients converted to elements of ``ring``.
...
        """
        P = self.parent()
        if P._names is None:
            Q = type(P)(ring, sparse=P._sparse)
        else:
            Q = type(P)(ring, names=P.variable_names(), sparse=P._sparse)
        return Q.element_class(Q, self._coeff_stream)

This doesn't follow the specification. However, if I change this, I seem to get coercion errors. I have to double check, though.

Update: sorry, I think it actually does, because __getitem__ converts to the correct ring in the end. The problem with #37975 must be elsewhere.

@mantepse
Copy link
Contributor Author

I'm afraid I found the problem. Consider, as an example, the lazy power series ring $B[[q, t]]$ over a base ring $B$. Let $B(\vec f)$ denote the fraction field of rational functions in variables $f_1, f_2, \dots$. Then it is probably unavoidable that we will sometimes multiply elements from $B(\vec f)$ and the polynomial ring $B[q, t]$, and, unfortunately, the result will live in $B[q, t](\vec f)$ instead of $B(\vec f)[q, t]$.

I admit I do not fully understand why this happens only rarely, and if so, it is unconsequential most of the time. Mostly, it seems to happen with exact series, where we do not make sure that their data (initial_coefficients and constant) live in the correct parent.

However, at least sometimes it is sheer luck that everything works out. For example, in LazyCauchyProductSeries.__pow__ we happen to coerce the initial coefficients of an exact series to the proper parent even if n=1.

I am not sure how to proceed. Do you think it makes sense to have another meeting?

@mantepse
Copy link
Contributor Author

Superseeded by #38108

@mantepse mantepse closed this Feb 11, 2025
vbraun pushed a commit to vbraun/sage that referenced this pull request Feb 18, 2025
sagemathgh-38108: Implicit function solver for lazy series
    
This pull request provides a method `define_implicitly` for lazy rings
that allows to produce lazy power series solutions for systems of
functional equations, in relatively great generality, superseding
sagemath#37033.

We use a special action to make sure that coercion between
`CoefficientRing` and its base ring works as desired. However, it is a
local change, if the current approach eventually shows its shortcomings.
(Alternative approach previously considered: Using a special functor to
control the pushout behaviors. The action approach was chosen as a way
to prevent potential conflicts with other construction functors that may
appear in the future.)

Dependencies: sagemath#38729
    
URL: sagemath#38108
Reported by: Martin Rubey
Reviewer(s): Martin Rubey, Travis Scrimshaw
vbraun pushed a commit to vbraun/sage that referenced this pull request Feb 21, 2025
sagemathgh-38108: Implicit function solver for lazy series
    
This pull request provides a method `define_implicitly` for lazy rings
that allows to produce lazy power series solutions for systems of
functional equations, in relatively great generality, superseding
sagemath#37033.

We use a special action to make sure that coercion between
`CoefficientRing` and its base ring works as desired. However, it is a
local change, if the current approach eventually shows its shortcomings.
(Alternative approach previously considered: Using a special functor to
control the pushout behaviors. The action approach was chosen as a way
to prevent potential conflicts with other construction functors that may
appear in the future.)

Dependencies: sagemath#38729
    
URL: sagemath#38108
Reported by: Martin Rubey
Reviewer(s): Martin Rubey, Travis Scrimshaw
@mantepse mantepse deleted the lazy_series/integration_experimental branch February 25, 2025 06:54
@mantepse mantepse restored the lazy_series/integration_experimental branch February 25, 2025 06:54
@mantepse mantepse deleted the lazy_series/integration_experimental branch February 25, 2025 06:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants