Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

A lazy alternative to @Max Alekseyev's excellent answer is to use one of Sage's symbolic solvers

A lazy alternative to @Max Alekseyev's excellent answer is to use one of Sage's symbolic solvers and to filter out the undeterminates returned by these solvers.

After running :

SymUnk=list(map(SR, (x, y, z)))
def foo():
    Sols=[]
    for fff in Combinations(Lf, 3):
        if len((B:=RR.ideal(fff).groebner_basis()))==3:
            SSol=solve([SR(u)==0 for u in B],
                           SymUnk,
                           solution_dict=True)
            Sol=[]
            for S in SSol:
                SRR={}
                for u in S.keys():
                    try:
                        SRR|={RR(u):RR(S[u])}
                    except:
                        continue
                Sol+=[SRR]
            Sols+=[Sol]
    return Sols

Running :

sage: %time Sols=foo()
CPU times: user 2min 1s, sys: 105 ms, total: 2min 1s
Wall time: 1min 48s
sage: len(Sols)
245

returns 245 solutions. However, many of them are duplicates, as can be shown by indexing them on the `groebner_b

A lazy alternative to @Max Alekseyev's excellent answer is to use one of Sage's symbolic solvers solvers, which already take care of non-zero-dimension solutions, and to filter out the undeterminates returned by these solvers.

After running :

SymUnk=list(map(SR, (x, y, z)))
def foo():
    Sols=[]
    for fff in Combinations(Lf, 3):
        if len((B:=RR.ideal(fff).groebner_basis()))==3:
            SSol=solve([SR(u)==0 for u in B],
                           SymUnk,
                           solution_dict=True)
            Sol=[]
            for S in SSol:
                SRR={}
                for u in S.keys():
                    try:
                        SRR|={RR(u):RR(S[u])}
                    except:
                        continue
                Sol+=[SRR]
            Sols+=[Sol]
    return Sols

Running :

sage: %time Sols=foo()
CPU times: user 2min 1s, sys: 105 ms, total: 2min 1s
Wall time: 1min 48s
sage: len(Sols)
245

returns 245 solutions. However, many of them are duplicates, as can be shown by indexing them on the `groebner_bgroebner_basis() solved :

def bar():
    Sols={}
    for fff in Combinations(Lf, 3):
        if len((B:=RR.ideal(fff).groebner_basis()))==3:
            SSol=solve([SR(u)==0 for u in B],
                           SymUnk,
                           solution_dict=True)
            Sol=[]
            for S in SSol:
                SRR={}
                for u in S.keys():
                    try:
                        SRR|={RR(u):RR(S[u])}
                    except:
                        continue
                Sol+=[SRR]
            Sols|={B:Sol}
    return Sols

sage: %time DSols=bar()
CPU times: user 2min 2s, sys: 84.4 ms, total: 2min 2s
Wall time: 1min 49s
sage: len(DSols)
156

These solutions to 156 distinct Groebner bases give 260 solutions :

sage: Set(flatten(list(DSols.values()))).cardinality()
260

In the present case, the laziness isn' too costly (2 minutes is way less than the time necessary tio rig up a general solution with polynomial rings tools...).

HTH

A lazy alternative to @Max Alekseyev's excellent answer is to use one of Sage's symbolic solvers, which already take care of non-zero-dimension solutions, and to filter out the undeterminates returned by these solvers.

After running :

SymUnk=list(map(SR, (x, y, z)))
def foo():
    Sols=[]
    for fff in Combinations(Lf, 3):
        if len((B:=RR.ideal(fff).groebner_basis()))==3:
            SSol=solve([SR(u)==0 for u in B],
                           SymUnk,
                           solution_dict=True)
            Sol=[]
            for S in SSol:
                SRR={}
                for u in S.keys():
                    try:
                        SRR|={RR(u):RR(S[u])}
                    except:
                        continue
                Sol+=[SRR]
            Sols+=[Sol]
    return Sols

Running :

sage: %time Sols=foo()
CPU times: user 2min 1s, sys: 105 ms, total: 2min 1s
Wall time: 1min 48s
sage: len(Sols)
245

returns 245 solutions. However, many of them are duplicates, as can be shown by indexing them on the groebner_basis() solved :

def bar():
    Sols={}
    for fff in Combinations(Lf, 3):
        if len((B:=RR.ideal(fff).groebner_basis()))==3:
            SSol=solve([SR(u)==0 for u in B],
                           SymUnk,
                           solution_dict=True)
            Sol=[]
            for S in SSol:
                SRR={}
                for u in S.keys():
                    try:
                        SRR|={RR(u):RR(S[u])}
                    except:
                        continue
                Sol+=[SRR]
            Sols|={B:Sol}
    return Sols

sage: %time DSols=bar()
CPU times: user 2min 2s, sys: 84.4 ms, total: 2min 2s
Wall time: 1min 49s
sage: len(DSols)
156

These solutions to 156 distinct Groebner bases give 260 solutions :

sage: Set(flatten(list(DSols.values()))).cardinality()
260

In the present case, the laziness isn' too isn't too costly (2 minutes is way less than the time necessary tio to rig up a general solution with polynomial rings tools...).

HTH

A lazy alternative to @Max Alekseyev's excellent answer is to use one of Sage's symbolic solvers, which already take care of non-zero-dimension solutions, and to filter out the undeterminates returned by these solvers.

After running :

SymUnk=list(map(SR, (x, y, z)))
def foo():
    Sols=[]
    for fff in Combinations(Lf, 3):
        if len((B:=RR.ideal(fff).groebner_basis()))==3:
            SSol=solve([SR(u)==0 for u in B],
                           SymUnk,
                           solution_dict=True)
            Sol=[]
            for S in SSol:
                SRR={}
                for u in S.keys():
                    try:
                        SRR|={RR(u):RR(S[u])}
                    except:
                        continue
                Sol+=[SRR]
            Sols+=[Sol]
    return Sols

Running :

sage: %time Sols=foo()
CPU times: user 2min 1s, sys: 105 ms, total: 2min 1s
Wall time: 1min 48s
sage: len(Sols)
245

returns 245 solutions. However, many of them are duplicates, as can be shown by indexing them on the groebner_basis() solved :

def bar():
    Sols={}
    for fff in Combinations(Lf, 3):
        if len((B:=RR.ideal(fff).groebner_basis()))==3:
            SSol=solve([SR(u)==0 for u in B],
                           SymUnk,
                           solution_dict=True)
            Sol=[]
            for S in SSol:
                SRR={}
                for u in S.keys():
                    try:
                        SRR|={RR(u):RR(S[u])}
                    except:
                        continue
                Sol+=[SRR]
            Sols|={B:Sol}
    return Sols

sage: %time DSols=bar()
CPU times: user 2min 2s, sys: 84.4 ms, total: 2min 2s
Wall time: 1min 49s
sage: len(DSols)
156

These solutions to 156 distinct Groebner bases give 260 solutions :

sage: Set(flatten(list(DSols.values()))).cardinality()
260

In the present case, the laziness isn't too costly (2 minutes is way less than the time necessary to rig up a general solution with polynomial rings tools...).

UPDATE :

It turns out that Sympy's solver does a better job than Sage's for dimension>0 ideals. An alternative way to write the solutions is :

 # List of pairs (triplets, base) with Groebnar base of length 3
Triplets = [[u, b]
            for u in Combinations(Lf, 3)
            if len(b:=Rr.ideal(u).groebner_basis())==3]
# List of *distinct* bases
Bases = list(uniq([u[1] for u in Triplets]))
# Solve them *IN SR* using sympy :
# Precompute the unknowns
Unk = list(map(lambda u:SR(u), Rr.gens()))
# Solve
SSols = [solve([SR(u) for u in v], Unk, algorithm="sympy") for v in Bases]

FWIW :

sage: %time SSols = [solve([SR(u) for u in v], Unk, algorithm="sympy") for v in Bases]
CPU times: user 3.27 s, sys: 0 ns, total: 3.27 s
Wall time: 3.27 s

Sympy's solver seems also faster than Sage's...

Since neither the triplets nor the bases can be used as dictionary keys ("unhashable"), we need a bit of record-keeping :

# Record the base-solutions link
BS=list(map(list, zip(Bases, SSols)))
# Link triplets to their solutions
TBS=[[u[0]]+BS[Bases.index(u[1])] for u in Triplets]

Expressing the SR solutions in the original ring is lazily left to the reader...

HTH