o
    8VaK                     @   s   d dl Z d dlmZmZmZ d dlmZ d dlmZ d dl	m
Z
 d dlmZ d dlmZ G dd	 d	Zg d
fddZdd Zdd Zdd ZdddZdd ZdS )    N)FpGroup
FpSubgroupsimplify_presentation)	FreeGroup)PermutationGroup)igcd)totientSc                   @   s   e Zd ZdZdd Zdd Zdd Zdd	 Zd
d Zdd Z	dd Z
dd Zdd Zdd Zdd Zdd Zdd Zdd Zdd Zd S )!GroupHomomorphismz
    A class representing group homomorphisms. Instantiate using `homomorphism()`.

    References
    ==========

    .. [1] Holt, D., Eick, B. and O'Brien, E. (2005). Handbook of computational group theory.

    c                 C   s(   || _ || _|| _d | _d | _d | _d S N)domaincodomainimages	_inverses_kernel_image)selfr   r   r    r   C/usr/lib/python3/dist-packages/sympy/combinatorics/homomorphisms.py__init__   s   
zGroupHomomorphism.__init__c           
      C   s   |   }i }t| j D ]}| j| }||v s|js|||< qt| jtr*|j}n|j	}|D ]?}||v s8|jr9q/| j
j}t| jtrN|j| ddd }n|}|D ]}	|	|v r_|||	  }qR|||	d  d  }qR|||< q/|S )z
        Return a dictionary with `{gen: inverse}` where `gen` is a rewriting
        generator of `codomain` (e.g. strong generator for permutation groups)
        and `inverse` is an element of its preimage

        N)imagelistr   keysis_identity
isinstancer   r   Zstrong_gens
generatorsr   identityZ_strong_gens_slp)
r   r   Zinverseskvgensgwpartssr   r   r   _invs   s2   

zGroupHomomorphism._invsc           	         s   ddl m} ddlm} t|||frot jtr j|} jdu r) 	  _ 
 } jj}t jtrB||ddd }n|}tt|D ]"}|| }|jrTqJ| jv ra| j|  }qJ| j|d  d  }qJ|S t|tr} fdd|D S dS )a  
        Return an element of the preimage of ``g`` or of each element
        of ``g`` if ``g`` is a list.

        Explanation
        ===========

        If the codomain is an FpGroup, the inverse for equal
        elements might not always be the same unless the FpGroup's
        rewriting system is confluent. However, making a system
        confluent can be time-consuming. If it's important, try
        `self.codomain.make_confluent()` first.

        r   Permutation)FreeGroupElementNr   c                       g | ]}  |qS r   )invert.0er   r   r   
<listcomp>k       z,GroupHomomorphism.invert.<locals>.<listcomp>)sympy.combinatoricsr(   sympy.combinatorics.free_groupsr)   r   r   r   reducer   r&   r   r   r   r   generator_productrangelenr   r   )	r   r"   r(   r)   r   r#   r!   ir%   r   r/   r   r+   ?   s.   



zGroupHomomorphism.invertc                 C   s   | j du r
|  | _ | j S )z0
        Compute the kernel of `self`.

        N)r   _compute_kernelr/   r   r   r   kernelm   s   

zGroupHomomorphism.kernelc           	      C   s   ddl m} | j}| }||ju rtdg }t|tr#t|j}nt	||dd}| 
  }| | |kri| }|| | |d  }||vra|| t|trZt|}nt	||dd}| | |ks8|S )Nr   r	   z9Kernel computation is not implemented for infinite groupsT)Znormalr   )sympyr
   r   orderInfinityNotImplementedErrorr   r   r   r   r   Zrandomr+   append)	r   r
   GZG_orderr!   Kr8   rr   r   r   r   r9   v   s,   




	z!GroupHomomorphism._compute_kernelc                 C   sP   | j du r%tt| j }t| jtr| j|| _ | j S t	| j|| _ | j S )z/
        Compute the image of `self`.

        N)
r   r   setr   valuesr   r   r   Zsubgroupr   )r   rD   r   r   r   r      s   
zGroupHomomorphism.imagec           	         s   | j vrt|ttfr fdd|D S td|jr  jjS  j} jj}t j t	rR j j
|dd}|D ]}| jv rE|| | }q7||d  d | }q7|S d}|jD ]!\}}|dk rf|| d }n|| }||| |  }|t|7 }qW|S )z*
        Apply `self` to `elem`.

        c                    r*   r   _applyr,   r/   r   r   r0      r1   z,GroupHomomorphism._apply.<locals>.<listcomp>z1The supplied element doesn't belong to the domainT)Zoriginalr   r   )r   r   r   tuple
ValueErrorr   r   r   r   r   r5   
array_formabs)	r   elemr   valuer!   r"   r8   _pr   r/   r   rF      s.   


zGroupHomomorphism._applyc                 C   s
   |  |S r   rE   )r   rK   r   r   r   __call__   s   
zGroupHomomorphism.__call__c                 C      |    dkS )z9
        Check if the homomorphism is injective

           )r:   r<   r/   r   r   r   is_injective      zGroupHomomorphism.is_injectivec                 C   sB   ddl m} |   }| j }||ju r||ju rdS ||kS )z:
        Check if the homomorphism is surjective

        r   r	   N)r;   r
   r   r<   r   r=   )r   r
   ZimZothr   r   r   is_surjective   s   
zGroupHomomorphism.is_surjectivec                 C   s   |   o|  S )z5
        Check if `self` is an isomorphism.

        )rR   rT   r/   r   r   r   is_isomorphism   rS   z GroupHomomorphism.is_isomorphismc                 C   rP   )zs
        Check is `self` is a trivial homomorphism, i.e. all elements
        are mapped to the identity.

        rQ   )r   r<   r/   r   r   r   
is_trivial   s   zGroupHomomorphism.is_trivialc                    s>      jstd fdd jD }t jj|S )z
        Return the composition of `self` and `other`, i.e.
        the homomorphism phi such that for all g in the domain
        of `other`, phi(g) = self(other(g))

        z?The image of `other` must be a subgroup of the domain of `self`c                    s   i | ]	}| |qS r   r   r-   r"   otherr   r   r   
<dictcomp>       z-GroupHomomorphism.compose.<locals>.<dictcomp>)r   is_subgroupr   rH   r   r   r   )r   rY   r   r   rX   r   compose   s   zGroupHomomorphism.composec                    sD   t |tr| jstd|} fdd|jD }t| j|S )zh
        Return the restriction of the homomorphism to the subgroup `H`
        of the domain.

        z'Given H is not a subgroup of the domainc                    s   i | ]}| |qS r   r   rW   r/   r   r   rZ      r1   z1GroupHomomorphism.restrict_to.<locals>.<dictcomp>)r   r   r\   r   rH   r   r   r   )r   Hr   r   r   r/   r   restrict_to   s
   zGroupHomomorphism.restrict_toc                 C   s   | |  stdg }t|  j}|jD ]-}| |}||vr+|| t|}|  jD ]}|| |vrC|||  t|}q0q|S )z
        Return the subgroup of the domain that is the inverse image
        of the subgroup ``H`` of the homomorphism image

        z&Given H is not a subgroup of the image)	r\   r   rH   r   r   r   r+   r?   r:   )r   r^   r!   PhZh_ir   r   r   r   invert_subgroup   s    


z!GroupHomomorphism.invert_subgroupN)__name__
__module____qualname____doc__r   r&   r+   r:   r9   r   rF   rO   rR   rT   rU   rV   r]   r_   rb   r   r   r   r   r   	   s"    
#.	 r   Tc                    s  t | tttfstdt  tttfstd| jtfddD r*tdt fdd|D r9td|rGt|tkrGtdt	t	|}|
 jgtt|   
fd	dD  tt|}|r}t|  |s}td
t|  |S )a  
    Create (if possible) a group homomorphism from the group ``domain``
    to the group ``codomain`` defined by the images of the domain's
    generators ``gens``. ``gens`` and ``images`` can be either lists or tuples
    of equal sizes. If ``gens`` is a proper subset of the group's generators,
    the unspecified generators will be mapped to the identity. If the
    images are not specified, a trivial homomorphism will be created.

    If the given images of the generators do not define a homomorphism,
    an exception is raised.

    If ``check`` is ``False``, don't check whether the given images actually
    define a homomorphism.

    zThe domain must be a groupzThe codomain must be a groupc                       g | ]}| vqS r   r   rW   )r   r   r   r0   %      z homomorphism.<locals>.<listcomp>zCThe supplied generators must be a subset of the domain's generatorsc                    rg   r   r   rW   )r   r   r   r0   '  rh   z+The images must be elements of the codomainz>The number of images must be equal to the number of generatorsc                    s   g | ]}| vr|qS r   r   rW   )r!   r   r   r0   1  s    z-The given images do not define a homomorphism)r   r   r   r   	TypeErrorr   anyrH   r7   r   extendr   dictzip_check_homomorphismr   )r   r   r!   r   checkr   )r   r   r!   r   homomorphism  s&   rp   c                    s   t  dr	 j}n
  j  j}|j fdd}|D ]4}t|trK|||}|d u rJ| }|||}|d u rJ|sJt	dn||j
}|sU dS q!dS )Nrelatorsc                    s   | j rS }| j}d}d}|t| k ra|| d }t tr0| | v r0 j| |  }n| | }|v rA|| |  }n|d v rQ||d  |  }|t|7 }|d7 }|t| k s|S )Nr   rQ   r   )r   rI   r7   r   r   r   indexrJ   )rB   r#   Zr_arrr8   jZpowerr%   r   r!   r   r   r   r   r   @  s&   	z#_check_homomorphism.<locals>._imagezCan't determine if the images define a homomorphism. Try increasing the maximum number of rewriting rules (group._rewriting_system.set_max(new_value); the current value is stored in group._rewriting_system.maxeqns)FT)hasattrrq   Zpresentationr   r   r   r   ZequalsZmake_confluentRuntimeErrorr   )r   r   r   Zrelsr   rB   r%   Zsuccessr   rt   r   rn   8  s(   




rn   c                    s   ddl m  ddlm} |t}|jt fdd| jD }| jd t	| ||}t| j
tkrD| j
t |_|S t| jg|_|S )z
    Return the homomorphism induced by the action of the permutation
    group ``group`` on the set ``omega`` that is closed under the action.

    r   r'   SymmetricGroupc                    s*   i | ]   fd dD  qS )c                    s   g | ]	} | A qS r   )rr   )r-   o)r"   omegar   r   r0     r[   z1orbit_homomorphism.<locals>.<dictcomp>.<listcomp>r   r-   r(   r   rz   r"   r   rZ     s   * z&orbit_homomorphism.<locals>.<dictcomp>)base)r2   r(    sympy.combinatorics.named_groupsrx   r7   r   r   r   Z_schreier_simsr   Zbasic_stabilizersr   r   )grouprz   rx   r   r   r^   r   r|   r   orbit_homomorphismt  s   r   c           	         s   ddl m  ddlm} t|}d}g dg| t|D ]}|| |kr2| ||< |d7 }qt|D ]
}||  |< q7||}t| fdd| jD }t| ||}|S )ab  
    Return the homomorphism induced by the action of the permutation
    group ``group`` on the block system ``blocks``. The latter should be
    of the same form as returned by the ``minimal_block`` method for
    permutation groups, namely a list of length ``group.degree`` where
    the i-th entry is a representative of the block i belongs to.

    r   r'   rw   NrQ   c                    s(   i | ]   fd dD qS )c                    s   g | ]
} | A  qS r   r   )r-   r8   )br"   rN   r   r   r0     s    z1block_homomorphism.<locals>.<dictcomp>.<listcomp>r   r{   r(   r   r   rN   r}   r   rZ     s   ( z&block_homomorphism.<locals>.<dictcomp>)	r2   r(   r   rx   r7   r6   r?   r   r   )	r   Zblocksrx   nmr8   r   r   r^   r   r   r   block_homomorphism  s&   	

r   c                 C   s  t | ttfstdt |ttfstdt | trGt |trGt| } t|}| j|jkrG| j |j krG|s<dS dt| || j|jfS |}| 	 }|	 }|t
ju rZtdt |trn|t
ju rhtd| \}}||ksx| j|jkr~|s|dS dS |s|}t|t|dkrdS t| j}t|t|D ]F}	t|	}
|
|jgt| jt|
   tt||
}t| ||rt |tr||
}
t| || j|
dd}| r|s dS d|f  S q|sdS dS )ax  
    Compute an isomorphism between 2 given groups.

    Parameters
    ==========

    G : A finite ``FpGroup`` or a ``PermutationGroup``.
        First group.

    H : A finite ``FpGroup`` or a ``PermutationGroup``
        Second group.

    isomorphism : bool
        This is used to avoid the computation of homomorphism
        when the user only wants to check if there exists
        an isomorphism between the groups.

    Returns
    =======

    If isomorphism = False -- Returns a boolean.
    If isomorphism = True  -- Returns a boolean and an isomorphism between `G` and `H`.

    Examples
    ========

    >>> from sympy.combinatorics import Permutation
    >>> from sympy.combinatorics.perm_groups import PermutationGroup
    >>> from sympy.combinatorics.free_groups import free_group
    >>> from sympy.combinatorics.fp_groups import FpGroup
    >>> from sympy.combinatorics.homomorphisms import group_isomorphism
    >>> from sympy.combinatorics.named_groups import DihedralGroup, AlternatingGroup

    >>> D = DihedralGroup(8)
    >>> p = Permutation(0, 1, 2, 3, 4, 5, 6, 7)
    >>> P = PermutationGroup(p)
    >>> group_isomorphism(D, P)
    (False, None)

    >>> F, a, b = free_group("a, b")
    >>> G = FpGroup(F, [a**3, b**3, (a*b)**2])
    >>> H = AlternatingGroup(4)
    >>> (check, T) = group_isomorphism(G, H)
    >>> check
    True
    >>> T(b*a*b**-1*a**-1*b**-1)
    (0 2 3)

    Notes
    =====

    Uses the approach suggested by Robert Tarjan to compute the isomorphism between two groups.
    First, the generators of ``G`` are mapped to the elements of ``H`` and
    we check if the mapping induces an isomorphism.

    z2The group must be a PermutationGroup or an FpGroupTz<Isomorphism methods are not implemented for infinite groups.F)FNrQ   )ro   )r   r   r   ri   r   r   rq   sortrp   r<   r
   r=   r>   Z_to_perm_groupZ
is_abelianr   r   r   	itertoolspermutationsr7   rk   r   rl   rm   rn   r+   rU   )r@   r^   isomorphismZ_HZg_orderZh_orderZh_isomorphismr   r!   Zsubsetr   Z_imagesTr   r   r   group_isomorphism  sZ   9 



 

r   c                 C   s   t | |ddS )a  
    Check if the groups are isomorphic to each other

    Parameters
    ==========

    G : A finite ``FpGroup`` or a ``PermutationGroup``
        First group.

    H : A finite ``FpGroup`` or a ``PermutationGroup``
        Second group.

    Returns
    =======

    boolean
    F)r   )r   )r@   r^   r   r   r   is_isomorphic!  s   r   )T)r   Zsympy.combinatorics.fp_groupsr   r   r   r3   r   Zsympy.combinatorics.perm_groupsr   Zsympy.core.numbersr   Zsympy.ntheory.factor_r   r;   r
   r   rp   rn   r   r   r   r   r   r   r   r   <module>   s       )<
$u