AbstractAlgebra.jl icon indicating copy to clipboard operation
AbstractAlgebra.jl copied to clipboard

Results of evaluate methods differ

Open karlwessel opened this issue 10 months ago • 13 comments

I think the following MWE is related to issue #1008 and I wonder why it is ok that the first form of evaluation works and the second doesn't:

julia> using AbstractAlgebra

julia> Rx, x = polynomial_ring(QQ, :x)
(Univariate polynomial ring in x over rationals, x)

julia> Ry, (y,) = polynomial_ring(QQ, [:y])
(Multivariate polynomial ring in 1 variable over rationals, AbstractAlgebra.Generic.MPoly{Rational{BigInt}}[y])

julia> evaluate(y, [x])
x

julia> evaluate(y, [1], [x])
ERROR: Cannot promote to common type
Stacktrace:
 [1] error(s::String)
   @ Base ./error.jl:35
 [2] promote(x::AbstractAlgebra.Generic.Poly{Rational{BigInt}}, y::AbstractAlgebra.Generic.MPoly{Rational{BigInt}})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/eNeG2/src/NCRings.jl:54
 [3] *(x::AbstractAlgebra.Generic.Poly{Rational{BigInt}}, y::AbstractAlgebra.Generic.MPoly{Rational{BigInt}})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/eNeG2/src/NCRings.jl:80
 [4] __evaluate(a::AbstractAlgebra.Generic.MPoly{…}, vars::Vector{…}, vals::Vector{…}, powers::Vector{…})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/eNeG2/src/MPoly.jl:991
 [5] _evaluate(a::AbstractAlgebra.Generic.MPoly{…}, S::AbstractAlgebra.Generic.MPolyRing{…}, R::AbstractAlgebra.Rationals{…}, vars::Vector{…}, vals::Vector{…})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/eNeG2/src/MPoly.jl:969
 [6] evaluate(a::AbstractAlgebra.Generic.MPoly{…}, vars::Vector{…}, vals::Vector{…})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/eNeG2/src/MPoly.jl:938
 [7] top-level scope
   @ REPL[11]:1
Some type information was truncated. Use `show(err)` to see complete types.

Or a more realistic use case where I only want to evaluate one of multiple variables:

julia> Ryz, (y, z) = polynomial_ring(QQ, [:y, :z])
(Multivariate polynomial ring in 2 variables over rationals, AbstractAlgebra.Generic.MPoly{Rational{BigInt}}[y, z])

julia> evaluate(y, [x, z])
x

julia> evaluate(y, [y, x])
y

julia> evaluate(y, [1], [x])
ERROR: Cannot promote to common type
Stacktrace:
 [1] error(s::String)
   @ Base ./error.jl:35
 [2] promote(x::AbstractAlgebra.Generic.Poly{Rational{BigInt}}, y::AbstractAlgebra.Generic.MPoly{Rational{BigInt}})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/eNeG2/src/NCRings.jl:54
 [3] *(x::AbstractAlgebra.Generic.Poly{Rational{BigInt}}, y::AbstractAlgebra.Generic.MPoly{Rational{BigInt}})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/eNeG2/src/NCRings.jl:80
 [4] __evaluate(a::AbstractAlgebra.Generic.MPoly{…}, vars::Vector{…}, vals::Vector{…}, powers::Vector{…})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/eNeG2/src/MPoly.jl:991
 [5] _evaluate(a::AbstractAlgebra.Generic.MPoly{…}, S::AbstractAlgebra.Generic.MPolyRing{…}, R::AbstractAlgebra.Rationals{…}, vars::Vector{…}, vals::Vector{…})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/eNeG2/src/MPoly.jl:969
 [6] evaluate(a::AbstractAlgebra.Generic.MPoly{…}, vars::Vector{…}, vals::Vector{…})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/eNeG2/src/MPoly.jl:938
 [7] top-level scope
   @ REPL[16]:1
Some type information was truncated. Use `show(err)` to see complete types.

julia> evaluate(y, [2], [x])
ERROR: Cannot promote to common type
Stacktrace:
 [1] error(s::String)
   @ Base ./error.jl:35
 [2] promote(x::AbstractAlgebra.Generic.Poly{Rational{BigInt}}, y::AbstractAlgebra.Generic.MPoly{Rational{BigInt}})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/eNeG2/src/NCRings.jl:54
 [3] *(x::AbstractAlgebra.Generic.Poly{Rational{BigInt}}, y::AbstractAlgebra.Generic.MPoly{Rational{BigInt}})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/eNeG2/src/NCRings.jl:80
 [4] __evaluate(a::AbstractAlgebra.Generic.MPoly{…}, vars::Vector{…}, vals::Vector{…}, powers::Vector{…})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/eNeG2/src/MPoly.jl:991
 [5] _evaluate(a::AbstractAlgebra.Generic.MPoly{…}, S::AbstractAlgebra.Generic.MPolyRing{…}, R::AbstractAlgebra.Rationals{…}, vars::Vector{…}, vals::Vector{…})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/eNeG2/src/MPoly.jl:969
 [6] evaluate(a::AbstractAlgebra.Generic.MPoly{…}, vars::Vector{…}, vals::Vector{…})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/eNeG2/src/MPoly.jl:938
 [7] top-level scope
   @ REPL[17]:1
Some type information was truncated. Use `show(err)` to see complete types.

karlwessel avatar Feb 25 '25 14:02 karlwessel

Note that if you make the examples even more realistic both versions of evaluate are consistent (in failing):

julia> evaluate(y+z, [y, x])
ERROR: Cannot promote to common type
Stacktrace:
 [1] error(s::String)
   @ Base ./error.jl:35
 [2] promote(x::AbstractAlgebra.Generic.MPoly{Rational{BigInt}}, y::AbstractAlgebra.Generic.Poly{Rational{BigInt}})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/lYBCD/src/NCRings.jl:54
 [3] *(x::AbstractAlgebra.Generic.MPoly{Rational{BigInt}}, y::AbstractAlgebra.Generic.Poly{Rational{BigInt}})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/lYBCD/src/NCRings.jl:80
 [4] evaluate(a::AbstractAlgebra.Generic.MPoly{Rational{BigInt}}, vals::Vector{RingElem})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/lYBCD/src/MPoly.jl:903
 [5] top-level scope
   @ REPL[18]:1

julia> evaluate(y+z, [x, z])
ERROR: Cannot promote to common type
Stacktrace:
 [1] error(s::String)
   @ Base ./error.jl:35
 [2] promote(x::AbstractAlgebra.Generic.Poly{Rational{BigInt}}, y::AbstractAlgebra.Generic.MPoly{Rational{BigInt}})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/lYBCD/src/NCRings.jl:54
 [3] *(x::AbstractAlgebra.Generic.Poly{Rational{BigInt}}, y::AbstractAlgebra.Generic.MPoly{Rational{BigInt}})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/lYBCD/src/NCRings.jl:80
 [4] evaluate(a::AbstractAlgebra.Generic.MPoly{Rational{BigInt}}, vals::Vector{RingElem})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/lYBCD/src/MPoly.jl:903
 [5] top-level scope
   @ REPL[19]:1

julia> evaluate(y+z, [1], [x])
ERROR: Cannot promote to common type
Stacktrace:
 [1] error(s::String)
   @ Base ./error.jl:35
 [2] promote(x::AbstractAlgebra.Generic.Poly{Rational{BigInt}}, y::AbstractAlgebra.Generic.MPoly{Rational{BigInt}})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/lYBCD/src/NCRings.jl:54
 [3] *(x::AbstractAlgebra.Generic.Poly{Rational{BigInt}}, y::AbstractAlgebra.Generic.MPoly{Rational{BigInt}})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/lYBCD/src/NCRings.jl:80
 [4] __evaluate(a::AbstractAlgebra.Generic.MPoly{…}, vars::Vector{…}, vals::Vector{…}, powers::Vector{…})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/lYBCD/src/MPoly.jl:997
 [5] _evaluate(a::AbstractAlgebra.Generic.MPoly{…}, S::AbstractAlgebra.Generic.MPolyRing{…}, R::AbstractAlgebra.Rationals{…}, vars::Vector{…}, vals::Vector{…})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/lYBCD/src/MPoly.jl:975
 [6] evaluate(a::AbstractAlgebra.Generic.MPoly{…}, vars::Vector{…}, vals::Vector{…})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/lYBCD/src/MPoly.jl:944
 [7] top-level scope
   @ REPL[20]:1
Some type information was truncated. Use `show(err)` to see complete types.

julia> evaluate(y+z, [2], [x])
ERROR: Cannot promote to common type
Stacktrace:
 [1] error(s::String)
   @ Base ./error.jl:35
 [2] promote(x::AbstractAlgebra.Generic.Poly{Rational{BigInt}}, y::AbstractAlgebra.Generic.MPoly{Rational{BigInt}})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/lYBCD/src/NCRings.jl:54
 [3] *(x::AbstractAlgebra.Generic.Poly{Rational{BigInt}}, y::AbstractAlgebra.Generic.MPoly{Rational{BigInt}})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/lYBCD/src/NCRings.jl:80
 [4] __evaluate(a::AbstractAlgebra.Generic.MPoly{…}, vars::Vector{…}, vals::Vector{…}, powers::Vector{…})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/lYBCD/src/MPoly.jl:997
 [5] _evaluate(a::AbstractAlgebra.Generic.MPoly{…}, S::AbstractAlgebra.Generic.MPolyRing{…}, R::AbstractAlgebra.Rationals{…}, vars::Vector{…}, vals::Vector{…})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/lYBCD/src/MPoly.jl:975
 [6] evaluate(a::AbstractAlgebra.Generic.MPoly{…}, vars::Vector{…}, vals::Vector{…})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/lYBCD/src/MPoly.jl:944
 [7] top-level scope
   @ REPL[21]:1
Some type information was truncated. Use `show(err)` to see complete types.

karlwessel avatar Feb 25 '25 14:02 karlwessel

Most of these failures are expected, e.g., evaluate(y+z, [2], [x]), since AbstractAlgebra will never create a new ring. (The result must be an element of R, Rx or Ryz, or any other ring that already exists.)

(Some of them might be made to work, like evaluate(y, [1], [x]).)

thofma avatar Feb 25 '25 14:02 thofma

You are right, if I create the resulting ring beforehand they work:

julia> Rxy, (x, y) = polynomial_ring(QQ, [:x, :y])
(Multivariate polynomial ring in 2 variables over rationals, AbstractAlgebra.Generic.MPoly{Rational{BigInt}}[x, y])

julia> evaluate(y+z, [2], [x])
2*x

I find it a bit irritating that the result depends on some global state...

karlwessel avatar Feb 25 '25 14:02 karlwessel

I tried your example, but it yields

julia> Rxy, (x, y) = polynomial_ring(QQ, [:x, :y])
(Multivariate polynomial ring in 2 variables over rationals, AbstractAlgebra.Generic.MPoly{Rational{BigInt}}[x, y])

julia> evaluate(y+z, [2], [x])
ERROR: UndefVarError: `z` not defined in `Main`

There are two rules for evaluation in AA: If you have a polynomial f in R[x,y], you can

  1. evaluate all variables at some elements s, t in some R-algebra S (meaning that elements of R and S can be multiplied, yielding elements of S).
  2. do a partial evaluation at an element s from a ring S, but the multiplication between elements of R[x, y] and S must work.

Since the product of an element of any ring R and any ring S is either an element of R or of S, this is not effected by any global state I think.

P.S.: There are some cases of partial evaluation that currently don't work, due to technical reasons.

thofma avatar Feb 25 '25 14:02 thofma

I tried your example, but it yields

Sorry, the example assumed the steps from the comments before. Here is the complete example:

julia> using AbstractAlgebra

julia> Ryz, (y, z) = polynomial_ring(QQ, [:y, :z])

julia> Rx, x = polynomial_ring(QQ, :x)
(Univariate polynomial ring in x over rationals, x)

julia> evaluate(y+z, [2], [x])
ERROR: Cannot promote to common type
Stacktrace:
 [1] error(s::String)
   @ Base ./error.jl:35
 [2] promote(x::AbstractAlgebra.Generic.Poly{Rational{BigInt}}, y::AbstractAlgebra.Generic.MPoly{Rational{BigInt}})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/lYBCD/src/NCRings.jl:54
 [3] *(x::AbstractAlgebra.Generic.Poly{Rational{BigInt}}, y::AbstractAlgebra.Generic.MPoly{Rational{BigInt}})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/lYBCD/src/NCRings.jl:80
 [4] __evaluate(a::AbstractAlgebra.Generic.MPoly{…}, vars::Vector{…}, vals::Vector{…}, powers::Vector{…})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/lYBCD/src/MPoly.jl:997
 [5] _evaluate(a::AbstractAlgebra.Generic.MPoly{…}, S::AbstractAlgebra.Generic.MPolyRing{…}, R::AbstractAlgebra.Rationals{…}, vars::Vector{…}, vals::Vector{…})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/lYBCD/src/MPoly.jl:975
 [6] evaluate(a::AbstractAlgebra.Generic.MPoly{…}, vars::Vector{…}, vals::Vector{…})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/lYBCD/src/MPoly.jl:944
 [7] top-level scope
   @ REPL[21]:1
Some type information was truncated. Use `show(err)` to see complete types.

julia> Rxy, (x, y) = polynomial_ring(QQ, [:x, :y])
(Multivariate polynomial ring in 2 variables over rationals, AbstractAlgebra.Generic.MPoly{Rational{BigInt}}[x, y])

julia> evaluate(y+z, [2], [x])
2*x

You are right however, this isn't caused by some global state, but by me replacing the variable x with a new generator.

karlwessel avatar Feb 25 '25 15:02 karlwessel

It is a bug that y + z does not throw an error (there is a missing parent check):

julia> parent(y) == parent(z)
false

I'll prepare a fix soon.

thofma avatar Feb 25 '25 15:02 thofma

It is a bug that y + z does not throw an error (there is a missing parent check):

julia> parent(y) == parent(z)
false

I'll prepare a fix soon.

Thanks, I created a separate issue for it at #2010.

karlwessel avatar Feb 25 '25 15:02 karlwessel

So if I would like the evaluation to work I would have to transform elements from both Rings to a new Ring via homomorphism? I think I need Oscar.jl to do this for Multivariate Polynomials right?

julia> using Oscar
  ___   ____   ____    _    ____
 / _ \ / ___| / ___|  / \  |  _ \   |  Combining ANTIC, GAP, Polymake, Singular
| | | |\___ \| |     / _ \ | |_) |  |  Type "?Oscar" for more information
| |_| | ___) | |___ / ___ \|  _ <   |  Manual: https://docs.oscar-system.org
 \___/ |____/ \____/_/   \_\_| \_\  |  Version 1.2.2

julia> Rxy, (x2, y2) = polynomial_ring(QQ, [:x, :y])
(Multivariate polynomial ring in 2 variables over QQ, QQMPolyRingElem[x, y])

julia> Ryz, (y1, z1) = polynomial_ring(QQ, [:y, :z])
(Multivariate polynomial ring in 2 variables over QQ, QQMPolyRingElem[y, z])

julia> Rxyz, (x3, y3, z3) = polynomial_ring(QQ, [:x, :y, :z])
(Multivariate polynomial ring in 3 variables over QQ, QQMPolyRingElem[x, y, z])

julia> h1 = hom(Ryz, Rxyz, [y3, z3])
Ring homomorphism
  from multivariate polynomial ring in 2 variables over QQ
  to multivariate polynomial ring in 3 variables over QQ
defined by
  y -> y
  z -> z

julia> h2 = hom(Rxy, Rxyz, [x3, y3])
Ring homomorphism
  from multivariate polynomial ring in 2 variables over QQ
  to multivariate polynomial ring in 3 variables over QQ
defined by
  x -> x
  y -> y

julia> evaluate(h1(y1 + z1), [2], [h2(x2)])
x + z

karlwessel avatar Feb 25 '25 16:02 karlwessel

Here is another case where the evaluation methods return different results, this time for UnivPoly:

julia> using AbstractAlgebra

julia> R = universal_polynomial_ring(QQ)
Universal Polynomial Ring over Rationals

julia> x = gen(R, :x)
x

julia> y = gen(R, :y)
y

julia> evaluate(x + y, [1], [QQ(1)])
y + 1

julia> evaluate(x + y, [2], [QQ(1)])
x + 1

julia> evaluate(x + y, [QQ(1), QQ(1)])
2//1

julia> evaluate(x + y, Vector{RingElement}([x, QQ(1)]))
x + 1

julia> evaluate(x + y, Vector{RingElement}([QQ(1), y]))
ERROR: MethodError: Cannot `convert` an object of type AbstractAlgebra.Generic.UnivPoly{Rational{BigInt}} to an object of type Rational{BigInt}
The function `convert` exists, but no method is defined for this combination of argument types.

Closest candidates are:
  convert(::Type{T}, ::T) where T<:Number
   @ Base number.jl:6
  convert(::Type{T}, ::T) where T
   @ Base Base.jl:126
  convert(::Type{T}, ::AbstractChar) where T<:Number
   @ Base char.jl:185
  ...

Stacktrace:
 [1] push!(a::Vector{Rational{BigInt}}, item::AbstractAlgebra.Generic.UnivPoly{Rational{BigInt}})
   @ Base ./array.jl:1260
 [2] evaluate(a::AbstractAlgebra.Generic.MPoly{Rational{BigInt}}, vals::Vector{RingElement})
   @ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/lYBCD/src/MPoly.jl:905
 [3] evaluate(a::AbstractAlgebra.Generic.UnivPoly{Rational{BigInt}}, A::Vector{RingElement})
   @ AbstractAlgebra.Generic ~/.julia/packages/AbstractAlgebra/lYBCD/src/generic/UnivPoly.jl:821
 [4] top-level scope
   @ REPL[12]:1

karlwessel avatar Feb 25 '25 16:02 karlwessel

Yes, the cleanest way is with proper morphisms. But most things can be done by just evaluating at the right things:

julia> ((y1 + z1)(y3, z3))(zero(Rxyz), x2(x3, one(Rxyz)), y3)
x + y

Here is another case where the evaluation methods return different results, this time for UnivPoly:

By "return different results", what do you mean? They all give correct results, but one of them errors, which is unfortunate (but I don't see any wrong results).

The evaluation code is very brittle when the input is inhomogeneous (as in your last example). If you stick to homogeneous evaluation points things "should work".

thofma avatar Feb 25 '25 17:02 thofma

I don't know what your actual use case is, but just in case let me point out that there is also universal_polynomial_ring which has "as many variables as you like", dynamically.

julia> R = @universal_polynomial_ring(QQ, [:x, :y])
Universal Polynomial Ring over Rationals

julia> f = x + y
x + y

julia> g = x * gen(R, :z)
x*z

UPDATE: sorry, I did not read everything and only now realize you also referenced universal_polynomial_ring.

fingolfin avatar Feb 25 '25 17:02 fingolfin

By "return different results", what do you mean? They all give correct results, but one of them errors, which is unfortunate (but I don't see any wrong results).

You are right, the results are correct, i just didn't expect the last line to error since the one before did and everything seems to be compatible in the mathematical sense.

karlwessel avatar Feb 25 '25 19:02 karlwessel

To summarize my issue as I see it:

  1. For x from Ring R1 and y from Ring R2: If evaluate(y, [x]) works I would expect evaluate(y, [1], [x]) to also work.
  2. For x and y from universal ring R: If evaluate(x + y, Vector{RingElement}([x, QQ(1)])) works I would expect evaluate(x + y, Vector{RingElement}([QQ(1), y])) to also work.

karlwessel avatar Feb 26 '25 12:02 karlwessel