sagemath / sage

Main repository of SageMath
https://www.sagemath.org
Other
1.44k stars 481 forks source link

principal and exponential specializations for symmetric functions #10930

Closed mantepse closed 4 years ago

mantepse commented 13 years ago

With this patch, you can now do:

sage: s = SymmetricFunctions(QQ).s()
sage: x = s[2,2,1]
sage: x.principal_specialization()
-q^4/(q^11 - 2*q^10 + q^8 + 2*q^6 - 2*q^5 - q^3 + 2*q - 1)
sage: x.principal_specialization(n=4)
q^11 + 2*q^10 + 3*q^9 + 4*q^8 + 4*q^7 + 3*q^6 + 2*q^5 + q^4
sage: x.exponential_specialization()
1/24*t^5
sage: x.exponential_specialization(q=QQ["q"].gen())
(q^4/(q^6 + 3*q^5 + 5*q^4 + 6*q^3 + 5*q^2 + 3*q + 1))*t^5

Implement the principal and exponential specializations for symmetric functions as given in Stanley, Enumerative Combinatorics, Section 7.8.

CC: @jbandlow @zabrocki @tscrim @darijgr

Component: combinatorics

Keywords: principal specialization, exponential specialization, symmetric functions

Author: Martin Rubey

Branch/Commit: cf9e0f2

Reviewer: Darij Grinberg, Mike Zabrocki

Issue created by migration from https://trac.sagemath.org/ticket/10930

mantepse commented 13 years ago

patch for principal and exponential specialization

mantepse commented 13 years ago
comment:1

Attachment: sf_principal_specialization-mr.patch.gz

mantepse commented 13 years ago

Description changed:

--- 
+++ 
@@ -1,8 +1,16 @@
-This patch implements the principal and exponential specialisations for symmetric functions as given in Stanley, Enumerative Combinatorics, Section 7.8.
+This patch -- in the combinat queue:
+
+http://combinat.sagemath.org/hgwebdir.cgi/patches/file/tip/sf_principal_specialization-mr.patch
+
+implements the principal and exponential specialisations for symmetric functions as given in Stanley, Enumerative Combinatorics, Section 7.8.

 Unfortunately, the patch currently has several problems:
+
 1) it seems that always the default code in sf/sfa.py is called, I don't know why
+
 2) I do not know how to give the desired default argument 1 the correct type in exponential_specialization
+
 3) I guess one should rather implement this for quasi-symmetric functions, but I do not know enough about these currently
+
 4) the documentation and sensible tests are still missing
jbandlow commented 13 years ago
comment:4

Hi Martin,

Thanks submitting this! A quick response to your points above. I'm not sure what you mean by point (1)... can you elaborate?

For point (2), you can use None as the default value, and then do

    if q is None:
        q = self.parent().base_ring().one()

as the first line of your code.

For point (3), quasisymmetric functions are still somewhat immature--in particular they are not in Sage proper. So this is not too big of a concern.

For point (4), yes, there should be more doc and tests. In particular, I find tests like

    all( e[mu].principal_specialization(4) == e[mu].expand(4)(1,q,q^2,q^3) for mu in Partitions(4) )

particularly convincing.

mantepse commented 13 years ago
comment:5

Hi Jason!

Many thanks for your quick comments.

1) Using trace I find

sage: S = SymmetricFunctions(QQ); s=S.s(); f = s[2,1]
sage: trace("f.principal_specialization()")
> <string>(1)<module>()

ipdb> s
--Call--
> /home/martin/SAGE/local/lib/python2.6/site-packages/sage/combinat/sf/sfa.py(1653)principal_specialization()
   1652 
-> 1653     def principal_specialization(self, n=infinity, q=var('q')):
   1654         r"""

ipdb> s
> /home/martin/SAGE/local/lib/python2.6/site-packages/sage/combinat/sf/sfa.py(1681)principal_specialization()
   1680         """
-> 1681         from sage.combinat.sf.sf import SymmetricFunctions
   1682         p = SymmetricFunctions(self.parent().base_ring()).p()

ipdb> 

but I was hoping that the principal specialisation from schur.py would be called.

2) Well, currently the actual value of 1 is not used at all (I test q==1 and call principal_specialization without passing q). So my question really is: some day somebody might implement something where the q is actually used. Is it better then if the default is None and the doc says, None should always mean one?

Thanks again!

mantepse commented 13 years ago
comment:6

I fixed indenting, bugs and added documentation and tests. Feedback welcome!

jbandlow commented 13 years ago
comment:7

Hi Martin, a few more points:

1) If I'm not mistaken, the function f in the exponential_specialization code for Schur functions is returning the product of the hooks of partition. This can also be done with Partition(partition).hook_product(1)

2) You do not give the result of the test in line 275 of the Schur function code.

3) See this and make sure that your patch satisfies these criteria. Please ask the sage-combinat list if you have any questions about these.

4) Once you have completed all of this, mark the patch as 'Needs Review' (by clicking the button at the bottom of the page). Then the 'Patchbot' will automatically apply and test your code.

Again, many thanks for your good work!

mantepse commented 13 years ago
comment:8

Replying to @jbandlow:

Jason, might it be that you were looking on a different version of the patch? There is no exponential_specialization for Schur functions (I use the generic version) and there is no test in line 275...

On the other hand, I just noticed several other problems with the code. Eg., s[1].exponential_specialization() doesn't work right now. I also should include tests for calling without any arguments...

jbandlow commented 13 years ago
comment:9

Replying to @mantepse:

Hi Martin,

Sorry for the delay. Yes I was looking at the wrong version of the patch last time. Everything on the combinat queue now looks good to me. I think that all that is left is for you to prepare the patch for sage as mentioned in my point (3) above.

mantepse commented 5 years ago

Branch: u/mantepse/specializations_for_symmetric_functions

mantepse commented 5 years ago

Commit: 366774b

mantepse commented 5 years ago

New commits:

366774brebase and correct
mantepse commented 5 years ago

Description changed:

--- 
+++ 
@@ -1,3 +1,24 @@
+Eight years having passed, it might be time to finish this.  I added tests that discovered a dozen glitches, and fixed them all.
+
+With this patch, you can now do:
+
+```
+sage: s = SymmetricFunctions(QQ).s()
+sage: x = s[2,2,1]
+sage: x.principal_specialization()
+-q^4/(q^11 - 2*q^10 + q^8 + 2*q^6 - 2*q^5 - q^3 + 2*q - 1)
+sage: x.principal_specialization(q=var("q"))
+-q^4/((q^4 - 1)*(q^3 - 1)*(q^2 - 1)*(q - 1)^2)
+sage: x.principal_specialization(n=4, q=var("q"))
+(q^5 - 1)*(q^4 - 1)*q^4/(q - 1)^2
+sage: x.exponential_specialization()
+1/24*t^5
+sage: x.exponential_specialization(q=QQ["q"].gen())
+(q^4/(q^6 + 3*q^5 + 5*q^4 + 6*q^3 + 5*q^2 + 3*q + 1))*t^5
+```
+
+old description:
+
 This patch -- in the combinat queue:

 http://combinat.sagemath.org/hgwebdir.cgi/patches/file/tip/sf_principal_specialization-mr.patch
7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 5 years ago

Branch pushed to git repo; I updated commit sha1. New commits:

1784ac6fix doctests
7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 5 years ago

Changed commit from 366774b to 1784ac6

d4d9e38a-6e64-40d7-a7f7-bd828eb9e0db commented 5 years ago
comment:18

Your description of the definition of principle_specialization is clear, but all of your exponential_specialization functions need a description. The one place that you have a description, it doesn't make sense to me because you seem to explain q-exponential_specialization, but not exponential_specialization and they don't clearly seem to be compatible by setting q=1. It wouldn't hurt to add a reference to the definition in the documentation either.

7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 5 years ago

Changed commit from 1784ac6 to 309e5da

7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 5 years ago

Branch pushed to git repo; I updated commit sha1. New commits:

309e5daadd documentation
mantepse commented 5 years ago
comment:20

Thank you, that was an oversight. I hope it is better now!

embray commented 5 years ago
comment:23

Moving tickets from the Sage 8.8 milestone that have been actively worked on in the last six months to the next release milestone (optimistically).

mantepse commented 4 years ago
comment:24

ping?

d4d9e38a-6e64-40d7-a7f7-bd828eb9e0db commented 4 years ago
comment:25

I think that this needs to be more compatible with Hall-Littlewood and Macdonald symmetric functions because this seems to be one place that they might be used. If the base ring already has a q in it, this creates an expression with two q's.

sage: Ht = SymmetricFunctions(QQ['q,t'].fraction_field()).macdonald().Ht()
sage: Ht[2].principal_specialization()
(q*q + 1)/(q^3 - q^2 - q + 1)
sage: _.parent()
Fraction Field of Univariate Polynomial Ring in q over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field

and I think that is wrong. What about:

try:
    q = self.base_ring()('q')
except:
    q = self.base_ring()["q"].fraction_field().gen()

Currently in 'powersum.py' you have just the last line.

Also maybe should have doc tests comparing the principal specialization to plethysm.

sage: one=f.parent().one()
sage: f.principal_specialization(q=q)==f(one/(1-q)).coefficient([])
True
sage: f.principal_specialization(n=4,q=q)==f(one*(1-q^4)/(1-q)).coefficient([])
True
tscrim commented 4 years ago
comment:26

I am not sure I agree with you Mike that the q for the principle specialization should be the same q as for the Macdonald. For example, at t=0, the Macdonald q counts the affine grading whereas the principle specialization q counts the principle grading after restricting to a finite-type representation. Because of this, I feel they should be different variables (and I would try to have a different name, perhaps q1, if 'q' in self.base_ring().variable_names_recursive()). Could you explain more why do you think they should be the same q?

d4d9e38a-6e64-40d7-a7f7-bd828eb9e0db commented 4 years ago
comment:27

I was thinking of applications like The Delta Conjecture (e.g. see Theorem 5.1). My comment is not specific to Macdonald polynomials though, it was just an example. Whatever variable you use, if it is in your base ring it seems that you should be using that letter and not adding another copy of it by default. If you want to use another variable in your specialization you can always add one.

tscrim commented 4 years ago
comment:28

I see. I think that violates the principle of least surprise: someone trying this method changes the variable in their base ring and suddenly the polynomial behaves differently. If you really wanted that base ring variable, then I think you should pass it as a parameter. So if you want a different variable name, then we should change the default name if q is already on the base ring.

d4d9e38a-6e64-40d7-a7f7-bd828eb9e0db commented 4 years ago
comment:29

From my understanding what you are saying you are recommending behavior like:

sage: SymmetricFunctions(QQ).s()[1].principal_specialization()
1/(1-q)
sage: SymmetricFunctions(SR).s()[1].principal_specialization()
1/(1-z)
mantepse commented 4 years ago
comment:30

I think it is in the long run more user friendly, if the rule that determines which name is used for a variable, or whether a symbol is actually reused, is as simple and predictable as possible. Therefore I would be rather against a "renaming" rule.

tscrim commented 4 years ago
comment:31

So you would rather it be a part of the base ring then Martin? Or same name (with possibly repeated variable name)?

mantepse commented 4 years ago
comment:32

I don't mind either way. I guess that having two q's which are different in one expression is more likely to be overlooked, so I guess that checking whether q is there is less bad. I would think that even the casual user might be aware that the base ring contains a q.

Is this OK for you?

A question: it turns out that the explicit fraction_field() in

try:
    q = self.base_ring()('q')
except:
    q = self.base_ring()["q"].fraction_field().gen()

is unnecessary. Should I keep it anyway?

tscrim commented 4 years ago
comment:33

If you are visually looking at output, then I agree, but if you are programming something, then it will break if you are doing something assuming that the output is a univariate polynomial ring. It is also much easier for the user to pass in the q of the base ring than to create a new polynomial ring with the variable.

Perhaps as a compromise, we raise an error if there is no q given but q is a variable name in the base ring? This is clearly the safest option, and it requires the user to know exactly what they are doing.

Also, I agree with removing the fraction_field since it is unnecessary.

mantepse commented 4 years ago
comment:34

Replying to @tscrim:

If you are visually looking at output, then I agree, but if you are programming something, then it will break if you are doing something assuming that the output is a univariate polynomial ring. It is also much easier for the user to pass in the q of the base ring than to create a new polynomial ring with the variable.

I agree!

Perhaps as a compromise, we raise an error if there is no q given but q is a variable name in the base ring? This is clearly the safest option, and it requires the user to know exactly what they are doing.

I think that is an excellent idea!

Also, I agree with removing the fraction_field since it is unnecessary.

will do.

7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 4 years ago

Branch pushed to git repo; I updated commit sha1. New commits:

3934475Merge branch 'develop' of git://trac.sagemath.org/sage into t/10930/specializations_for_symmetric_functions
7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 4 years ago

Changed commit from 309e5da to 3934475

mantepse commented 4 years ago
comment:36

Perhaps as a compromise, we raise an error if there is no q given but q is a variable name in the base ring? This is clearly the safest option, and it requires the user to know exactly what they

are doing.

I think that is an excellent idea!

Unfortunately, it doesn't work. The base ring could be the symbolic ring.

tscrim commented 4 years ago
comment:37

Replying to @mantepse:

Unfortunately, it doesn't work. The base ring could be the symbolic ring.

We can treat the symbolic ring as special. What I was think was doing

try:
    if 'q' in self.base_ring().variable_names_recursive():
        raise ValueError("q is already a variable name, so you must give the parameter q")
    else:
        q = self.base_ring()['q'].gen()
except AttributeError:
    q = self.base_ring()['q'].gen()

For this, they symbolic ring would be treated the same as QQ, and we would get a univariate polynomial ring over SR. The other option is just explicitly test for self.base_ring() is SR and just let q = SR.var('q').

7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 4 years ago

Changed commit from 3934475 to c5f5b61

7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 4 years ago

Branch pushed to git repo; I updated commit sha1. New commits:

c5f5b61raise error if q is in the base ring, add comment about plethysm
mantepse commented 4 years ago
comment:39

emails crossed :-)

I think it's OK to require q to be passed explicitely for the symbolic ring. I would rather not test explicitely for SR, because it is quite possible to have, for example and some time in future, a polynomial ring which contains all variables.

Since not all rings have variable_names_recursive, I simply try to convert "q" to a ring element, as Mike proposed.

Are you OK with this version?

tscrim commented 4 years ago
comment:40

This is acceptable to me. Mike?

d4d9e38a-6e64-40d7-a7f7-bd828eb9e0db commented 4 years ago
comment:41

I'll need a little time to try it out. "explicitely" is spelled wrong and I want to check that all coercions are correct.

As long as there is an option to use one of the variables in the ring in case that is what is desired that should be ok.

7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 4 years ago

Changed commit from c5f5b61 to cff572b

7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 4 years ago

Branch pushed to git repo; I updated commit sha1. New commits:

cff572bexplicitely -> explicitly
mantepse commented 4 years ago
comment:43

Thanks for spotting the misspelling! I created ticket #28843 for the other occurrences of "explicitely" :-)

mantepse commented 4 years ago
comment:44

The startup modules plugin says:

========== startup_modules ==========
git checkout patchbot/ticket_merged
/local/sage-patchbot/sage/sage -c ''
Total count: 1687
New:
sage.combinat.q_analogues
====================

I am guessing that this happens because I import q_binomial and q_factorial in elementary.py and homogeneous.py. Should I rather import them locally?

tscrim commented 4 years ago
comment:45

I think it is better to do them locally as I don't see the extra import making a timing difference.

7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 4 years ago

Changed commit from cff572b to a19c6f5

7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 4 years ago

Branch pushed to git repo; I updated commit sha1. New commits:

a19c6f5fix default values for eponential specialisation, import q_analogues locally
mantepse commented 4 years ago
comment:47

I just noticed that I overlooked the default values for the exponential specialisation.

I factored out the following little helper function, but I do not know where to put it:

def get_variable(ring, name):
    try:
        ring(name)
    except TypeError:
        return ring[name].gen()
    else:
        raise ValueError("the variable %s is in the base ring, pass it explicitly" % name)

This could also be used in theta_qt:

    def theta_qt(self, q=None, t=None):
        r"""
        Return the image of ``self`` under the `q,t`-deformed theta
        endomorphism which sends `p_k` to `\frac{1-q^k}{1-t^k} \cdot p_k`
        for all positive integers `k`.

        In general, this is well-defined outside of the powersum basis only
        if the base ring is a `\QQ`-algebra.

        INPUT:

        - ``q``, ``t`` -- parameters (default: ``None``, in which case 'q'
          and 't' are used)

...

        """
        parent = self.parent()
        BR = parent.base_ring()
        p = parent.realization_of().power()
        p_self = p(self)
        if t is None:
            if hasattr(parent,"t"):
                t = parent.t
            else:
                t = BR(QQ['t'].gen())
        if q is None:
            if hasattr(parent,"q"):
                q = parent.q
            else:
                q = BR(QQ['q'].gen())

same in omega_qt, nabla, scalar_qt, scalar_jack

d4d9e38a-6e64-40d7-a7f7-bd828eb9e0db commented 4 years ago
comment:48

No it can't be. For theta_qt the q and the t must be in the base_ring.

mantepse commented 4 years ago
comment:49

Replying to @zabrocki:

No it can't be. For theta_qt the q and the t must be in the base_ring.

You are right, I am sorry.