This answer shows why we have the irritating, counterintuitive results in between, and tries to give a solution for a "correct lift". Let us consider a definition of $H,G$ in a more common manner.
H.<a,b,c,d> = FreeGroup()
G = H.quotient([a*b, c*d])
J = G.simplification_isomorphism()
C = J.codomain()
print(f"H is {H}")
print(f"G is {G}")
print(f"C is {C}")
print(f"J is {J}")
print(f"Is G the domain of definition of J? {J.domain() == G}")
print(f"Is C the codomain of J? {J.codomain() == C}")
The above code delivers the following information:
H is Free Group on generators {a, b, c, d}
G is Finitely presented group < a, b, c, d | a*b, c*d >
C is Finitely presented group < a, c | >
J is Generic morphism:
From: Finitely presented group < a, b, c, d | a*b, c*d >
To: Finitely presented group < a, c | >
Defn: a |--> a
b |--> a^-1
c |--> c
d |--> c^-1
Is G the domain of definition of J? True
Is C the codomain of J? True
sage:
Now let us say some words on the way we can represent an element of $H$.
Consider the element $abcd\bar a\bar b\bar c\bar d$. (A bar denotes the inverse, it is simpler to type it.)
One possibility is to type:
sage: a * b * c * d * a^-1 * b^-1 * c^-1 * d^-1
a*b*c*d*a^-1*b^-1*c^-1*d^-1
sage: _.parent()
Free Group on generators {a, b, c, d}
But an other way is, using some sage in between representation as a tuple:
sage: H( (1, 2, 3, 4, -1, -2, -3, -4) )
a*b*c*d*a^-1*b^-1*c^-1*d^-1
sage: (a * b * c * d * a^-1 * b^-1 * c^-1 * d^-1).Tietze()
(1, 2, 3, 4, -1, -2, -3, -4)
And the tuple plugged in in the first line above is accepted, the entries are numbers, these numbers are insensible w.r.t. the group we are using for them. It may be $H$, or $G$, or $C$, the main point is to not use numbers bigger (in modulus) then the number of generators. So we can also write
sage: G( (1, 2, 3, 4, -1, -2, -3, -4) )
a*b*c*d*a^-1*b^-1*c^-1*d^-1
and there is no error (but also no simplification). But we cannot write
sage: C( (1, 2, 3, 4, -1, -2, -3, -4) )
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
and there is a long error message following.
We consider now $d\in H$. It is mapped to $d$ in the quotient. To distinguish the two $d$'s in code we write d
or H(d)
for the element $d\in H$, and G(d)
for the element $d$ in the quotient $G$. Then $J$ maps $G(d)$ into...
sage: J(G(d))
c^-1
sage: C( (-2,) )
c^-1
sage: # don't try J(G(d)) == C( (-2,) )
So $J(d)=\bar c$, an equality inside $C$. Now we would like to see this element correctly lifted inside $H$. The "obvious" $H(C(-2))$ is taking the "Tietze-tuple" (-2, )
and does the best to get an element inside $h$ for it.
To have the right lift, we may want to define a morphism from $C$ to $H$.
sage: j = C.Hom(H)( (a, c) )
sage: j(C(a))
a
sage: j(C(c))
c
The definition of the morphism is as follows, in human words. Consider some homomorphism from $C$ to $H$, this is the part with C.Hom(H)
. Which one? The one that maps the generators of $C$, in their given order to the elements of the tuple (a, c)
, also in this order. And with these objects...
- my expectation was that $j(J(G(d)))$ should work. No, it is not working. (The reason is maybe that somehow as above,
J(G(d)) == C( (-2,) )
evaluates to False
.) so let us oblige sage to do the right thing by inserting the two inverse operations of getting the Tietze representations, and building the $C$-element for such a representation:
sage: j( C( J(G(a)).Tietze() ) )
a
sage: j( C( J(G(b)).Tietze() ) )
a^-1
sage: j( C( J(G(c)).Tietze() ) )
c
sage: j( C( J(G(d)).Tietze() ) )
c^-1
sage: j( C( J(G(a * b * c * d * a^-1 * b^-1 * c^-1 * d^-1)).Tietze() ) )
1
I'm sorry, i don't have a better solution...