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

Lazy species #38544

Draft
wants to merge 57 commits into
base: develop
Choose a base branch
from
Draft

Lazy species #38544

wants to merge 57 commits into from

Conversation

mantepse
Copy link
Collaborator

@mantepse mantepse commented Aug 21, 2024

We provide an implementation of combinatorial species based on the lazy series framework and #38446.

dependencies: #38974

@mantepse mantepse added c: combinatorics gsoc: 2024 Tag for GSoC2024 issues/PRs and removed s: needs review labels Aug 21, 2024
@mantepse mantepse marked this pull request as draft August 21, 2024 14:30
Copy link

github-actions bot commented Aug 21, 2024

Documentation preview for this PR (built with commit b62aa20; changes) is ready! 🎉
This preview will update shortly after each push to this PR.

Comment on lines 148 to 154
def add_structures(labels):
yield from self.structures(labels)
yield from other.structures(labels)

result = super()._add_(other)
result.structures = add_structures
return result
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@tscrim, I'm not sure whether this is the best way to do it. I tried to create a separate class SumSpeciesElement inheriting from LazySpeciesElement that only overrides the method structures (and will override generating_series, etc.), but got stuck.

I think I would like to turn the result of super()._add_(other) into a SumSpeciesElement instance.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

One other thing: I am not sure, but perhaps it would actually be nice to take some decisions of LazyModuleElement._add_ into account. For example, if one of the arguments is zero, I might not want to return a SumSpeciesElement, but rather the other summand.

However, this smells badly like duplicating a lot of code.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Rethinking this: I guess it happens next to never that we are in one of the cases treated specially by LazyModuleElement._add_ (or _mul_, etc.) So maybe my afterthought is thinking about the empty set.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Does the following look correct to you? At least it works. I admit that the arguments super requires are not super-clear to me.

class LazySpeciesElement(LazyCompletionGradedAlgebraElement):
...
    def _add_(self, other):
        return SumSpeciesElement(self, other)
...
class SumSpeciesElement(LazySpeciesElement):
    def __init__(self, left, right):
        self._left = left
        self._right = right
        coeff_stream = super(LazySpeciesElement, type(left))._add_(left, right)._coeff_stream
        super().__init__(self.parent(), coeff_stream)

    def structures(self, labels):
        yield from self._left.structures(labels)
        yield from self._right.structures(labels)

@mantepse
Copy link
Collaborator Author

This is now mostly functional. The next decision is how to go about replacing the old species framework - @fchapoton, it would be great to have your input on this!

There is some functionality which is definitively missing. I know of the following:

  1. relabelling of structures
  2. generating isotypes
  3. the idea of SpeciesStructure, which provides a parent for structures and may make it easier to manipulate them
  4. functorial composition
  5. obtain a system of equations for a recursively defined species

1-3 are probably very easy, I think that 5 does not really belong to the species code, but should rather be a feature of the lazy framwork. Most likely, there is more.

More questions:

  • should we try to transfer the doctests into the new framework?
  • should we split the file into more components? If so, along which lines?

@mantepse
Copy link
Collaborator Author

I realize that I have no idea whether a species structure should have any structure at all. I see the following two options:

  1. structures indeed returns just arbitrary objects, only the species knows how to relabel a structure and how to check whether two structures are isomorphic.
  2. structures returns SpeciesStructures. Such a SpeciesStructure
    • knows the species which produced it
    • knows its labels
    • knows how to print itself
    • knows how to relabel itself
    • can test whether it is isomorphic to another structure
    • can possibly return a canonical labelling of itself.

Special species classes, such as SumSpecies, ProductSpecies and CompositionSpecies, may provide additional methods, for example it could know the summands, factors, etc.

The current approach is the second one, and I'm leaning towards it, although this doesn't quite follow the math. I think we could additionally add a facade option to the structures method or the species constructor itself.

@mantepse
Copy link
Collaborator Author

I am encountering yet another design question, @tscrim, I think that's for you :-)

I added a method restrict(self, min_degree=None, max_degree=None) to LazyModuleElement, which removes all elements of degree smaller than min_degree and larger than max_degree, similar to truncate.

Now consider a "special" lazy species, such as GraphSpecies(LazySpeciesElement) or SetSpecies(LazySpeciesElement). One reason to have them is their special implementation of structures and isotypes, which should yield graphs or sets, rather than the generic coset representatives corresponding to the molecular decomposition. For example, in my private branch I have

sage: from sage.rings.lazy_species import LazySpecies
sage: L = LazySpecies(QQ, "X")
sage: P = L.SetPartitions()
sage: list(P.structures([1,2,3]))
[{{1, 2, 3}}, {{1, 2}, {3}}, {{1, 3}, {2}}, {{1}, {2, 3}}, {{1}, {2}, {3}}]

What I'd like to have is that restriction preserves this, but of course, it doesn't:

sage: list(P.restrict(3, 3).structures([1,2,3]))
[(E_3, ((1, 2, 3),), 0),
 (E_3, ((1, 2, 3),), 1),
 (X*E_2, ((2,), (1, 3))),
 (X*E_2, ((3,), (1, 2))),
 (X*E_2, ((1,), (2, 3)))]

I think that restrict cannot return an instance of SetPartitionSpecies because, the way I defined it, SetPartitionSpecies is the species of set partitions of arbitrary finite sets.

I guess we'll have to have a video call to sort this out, I'm afraid my explanation is as unclear as possible :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: combinatorics gsoc: 2024 Tag for GSoC2024 issues/PRs
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant