diff --git a/M2/Macaulay2/m2/matrix2.m2 b/M2/Macaulay2/m2/matrix2.m2 index 01bcad74f35..a51ccbcbf79 100644 --- a/M2/Macaulay2/m2/matrix2.m2 +++ b/M2/Macaulay2/m2/matrix2.m2 @@ -396,6 +396,7 @@ Matrix \\ Matrix := Matrix => (g, f) -> quotient'(f, g) quotient(Matrix, Matrix) := Matrix => opts -> (f, g) -> ( -- given f: A-->C and g: B-->C, then find (f//g): A-->B such that g o (f//g) + r = f if target f != target g then error "quotient: expected maps with the same target"; + if f == 0 then return map(source g, source f, 0); c := runHooks((quotient, Matrix, Matrix), (opts, f, g), Strategy => opts.Strategy); if c =!= null then c else error "quotient: no method implemented for this type of input") @@ -408,11 +409,19 @@ addHook((quotient, Matrix, Matrix), Strategy => Default, MinimalGenerators => opts.MinimalGenerators }; map(source g, source f, homomorphism(homomorphism'(f, opts) // Hom(source f, g, opts))))) --- FIXME: this is still causing unreasonable slow downs, e.g. for (large m) // (scalar) -addHook((quotient, Matrix, Matrix), Strategy => "Reflexive", (opts, f, g) -> if f == 0 or isFreeModule source f then ( - L := source f; -- result may not be well-defined if L is not free +addHook((quotient, Matrix, Matrix), Strategy => "Reflexive", (opts, f, g) -> ( + L := source f; M := target f; N := source g; + -- TODO: should this be a separate strategy? + if not isFreeModule L then return ( + -- result may not be well-defined if L is not free, + -- unless the composition h * syz p is zero + p := coverMap L; + h := quotient(f * p, g, Strategy => "Reflexive"); + -- TODO: does h * gens ker p != 0 suffice? + if h * inducedMap(source p, kernel p) == 0 then map(N, L, h)); + -- if M.?generators then ( M = cokernel presentation M; -- this doesn't change the cover ); diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/quotient-remainder-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/quotient-remainder-doc.m2 index 1f44bec5d5c..1126dd757e9 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/quotient-remainder-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/quotient-remainder-doc.m2 @@ -152,8 +152,8 @@ Node reduction of @TT "f"@ modulo a Gröbner basis for the image of @TT "dual g"@. See @TO quotient@ for the dual notion. - If the remainder @TT "f - h*g"@ is zero, then the quotient @TT "g\\f"@ - satisfies the equation @TT "f === (g\\f) * g"@. Otherwise, the equation + If the remainder @TT "f - h*g"@ is zero, then the quotient @TT "g\\\\f"@ + satisfies the equation @TT "f === (g\\\\f) * g"@. Otherwise, the equation @TT "h * g + r === f"@ will hold, where @TT "r"@ is the map provided by @TO remainder'@. Example diff --git a/M2/Macaulay2/packages/Macaulay2Doc/ov_system.m2 b/M2/Macaulay2/packages/Macaulay2Doc/ov_system.m2 index 34bd142e82d..d737a7fa59c 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/ov_system.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/ov_system.m2 @@ -1870,7 +1870,12 @@ document { Key => "loadedFiles", PARA { "After each source file is successfully loaded, the full path to the file is stored in the hash table ", TO "loadedFiles", ". It is stored as the value, with the corresponding key being a small integer, consecutively assigned, starting at 0." }, - EXAMPLE "peek loadedFiles"} + EXAMPLE lines /// + loadedFiles#0 + #loadedFiles + ///, + --SeeAlso => { "filesLoaded" }, + } document { Key => "homeDirectory", Headline => "the home directory of the user", diff --git a/M2/Macaulay2/tests/normal/quotient.m2 b/M2/Macaulay2/tests/normal/quotient.m2 index 44276ba8483..93dac5ce5cf 100644 --- a/M2/Macaulay2/tests/normal/quotient.m2 +++ b/M2/Macaulay2/tests/normal/quotient.m2 @@ -40,3 +40,117 @@ assert(f \\ h == g) assert((f\\h) * f == h) assert(g // h == 0) -- does it always happen that this is zero if lift can't occur? Probably not assert(h \\ f == 0) + + +-- tests which show that the "Reflexive" strategy doesn't always work +S = QQ[x_0,x_1,x_2] --/ sum(3, i -> x_i^3) +a = x_0 + x_1 +b = x_0^2-x_0*x_1+x_1^2 +A = matrix {{x_2, a}, {b, -x_2^2}} +B = matrix {{x_2^2, a}, {b, -x_2}} + +f = map(S^2 / a, S^2 / a, A * B) +f' = map(S^2 / a, S^2, A * B) +g = map(S^2 / a, S^2, A) +h = map(S^2 / a, S^2 / a, B) +assert all({f', f, g, h}, isWellDefined) + +-- f does not factor through g without a remainder +assert not isSubset(image homomorphism' f, image Hom(source f, g)) + +-- here is the example +q = f // g +assert(isWellDefined q and q == 0) + +q = quotient(f, g, Strategy => Default) +assert(isWellDefined q and q == 0) + +-- the Reflexive strategy is not applicable here +assert try ( quotient(f, g, Strategy => "Reflexive"); false ) else true + +-- but f' does (left) factor through g +assert isSubset(image homomorphism' f, image Hom(g, target f)) + +q = g \\ f' +assert isWellDefined q +assert(f' == q * g) +assert(h == q) + +q = quotient'(f', g, Strategy => Default) +assert isWellDefined q +assert(f' == q * g) +assert(h == q) + +assert try ( quotient'(f', g, Strategy => "Reflexive"); false ) else true + + +-- this is an example where the improved "Reflexive" strategy works +f = homomorphism random Hom(image(A | B), S^2) +g = homomorphism random Hom(image(B | A), S^2) +h = homomorphism random Hom(image(A | B), image(B | A)) +f = g * h +assert all({f, g}, isWellDefined) + +-- f factors through g without a remainder +assert isSubset(image homomorphism' f, image Hom(source f, g)) + +-- here is the example +q = f // g +assert(isWellDefined q and f == g * q) + +q = quotient(f, g, Strategy => Default) +assert(isWellDefined q and f == g * q) + + +-- this is an example where the improved "Reflexive" strategy works +R = ZZ/2[x,y,z,w] +g = matrix {{x, x+y+z}, {x+y+z, z}} +f = g * matrix {{y^2}, {z^2}} +f = inducedMap(target f / (x+y+z), source f / (x+y+z), f) +g = inducedMap(target g / (x+y+z), source g, g) +isWellDefined f +isWellDefined g + +-- f does not factor through g without a remainder +assert not isSubset(image homomorphism' f, image Hom(source f, g)) + +q = quotient(f, g, Strategy => Default) +assert(isWellDefined q and q == 0) + +assert try ( quotient(f, g, Strategy => "Reflexive"); false ) else true + +---- +clearAll +B = QQ[a..d] +f = prune map(B^1, image(map(B^{{-2}, 3:{-3}}, B^{{-3}, 3:{-4}, {-3}, 3:{-4}, {-3}, 3:{-4}, {-3}, 3:{-4}}, {{a, 0, 0, 0, b, 0, 0, 0, c, 0, 0, 0, d, 0, 0, 0}, {0, a, 0, 0, 0, b, 0, 0, 0, c, 0, 0, 0, d, 0, 0}, {0, 0, a, 0, 0, 0, b, 0, 0, 0, c, 0, 0, 0, d, 0}, {0, 0, 0, a, 0, 0, 0, b, 0, 0, 0, c, 0, 0, 0, d}})),{{a*b*c-a^2*d, a*b^3-a^3*c, a^2*c^2-a*b^2*d, a*c^3-a*b*d^2, b^2*c-a*b*d, b^4-a^2*b*c, a*b*c^2-b^3*d, b*c^3-b^2*d^2, b*c^2-a*c*d, b^3*c-a^2*c^2, a*c^3-b^2*c*d, c^4-b*c*d^2, b*c*d-a*d^2, b^3*d-a^2*c*d, a*c^2*d-b^2*d^2, c^3*d-b*d^3}}) +g = prune map(B^1, image(map(B^1, B^{4:{-1}}, {{a, b, c, d}})), {{a, b, c, d}}) +assert isSubset(image homomorphism' f, image Hom(source f, g)) +elapsedTime assert(f == g * quotient(f, g, Strategy => Default)) +elapsedTime assert(f == g * quotient(f, g, Strategy => "Reflexive")) + +---- +S = QQ[x] +f = random(S^1/x, S^1/x^3) +g = random(S^1/x, S^1/x^2) +elapsedTime assert(f == g * quotient(f, g, Strategy => Default)) +elapsedTime assert(f == g * quotient(f, g, Strategy => "Reflexive")) + +---- +S = QQ[x,y]; +M = coker matrix {{x,y},{-y,x}}; +g = random(M, M) +f = g * random(M, M) +q = quotient(f, g, Strategy => "Reflexive") +assert(isWellDefined q and f == g * q) + +---- +S = QQ[x,y,z] +M = S^{-2} +N = S^{-3} +P = truncate(3, S^1) +h = homomorphism random(3, Hom(P, N)) +g = homomorphism random(0, Hom(N, M)) +f = g * h +assert all({f, g, h}, isWellDefined) +q = quotient(f, g, Strategy => "Reflexive") +assert(isWellDefined q and f == g * q) diff --git a/M2/cmake/build-libraries.cmake b/M2/cmake/build-libraries.cmake index 2c1e1608aa5..10471a9d900 100644 --- a/M2/cmake/build-libraries.cmake +++ b/M2/cmake/build-libraries.cmake @@ -511,7 +511,7 @@ ExternalProject_Add(build-msolve TEST_EXCLUDE_FROM_MAIN ON STEP_TARGETS install test ) -_ADD_COMPONENT_DEPENDENCY(programs msolve "gmp;mpfr;flint" MSOLVE_FOUND) +_ADD_COMPONENT_DEPENDENCY(programs msolve "gmp;mpfr;flint" MSOLVE) # https://numpi.dm.unipi.it/software/mpsolve