Exemplo n.º 1
0
    def bfs(source):
        """
        breath first search of solutions
        :return: new starting point or None
        """
        global dopt, lopt, depth, miniter, maxiter

        EXPLORED = {}
        # bfs search from minnode
        EXPLORED[source.elem.tostring()] = True
        roots = [source]
        update_solutions(source)
        logger.debug("loop starts with the first trial {:s} => {:g}".format(str(source.elem.flatten()), source.elem_dist))
        updated = True

        depth = 0
        while depth < miniter or (updated and depth < maxiter):
        # while depth < maxiter:
            # update roots, generator first
            t0 = timer()
            # clear update flag
            updated = False
            # change depth tracker
            depth += 1
            # going down on the tree by iteration
            new_roots = []

            for root in roots:
                for gen, elem in root.children:
                    hashcode = elem.tostring()
                    if not hashcode in EXPLORED:
                        EXPLORED[hashcode] = True
                        t = SLNode(elem, SLNODE_CACHE, parent=gen, grandpa=root.parent)
                        new_roots.append(t)

                        # if in min iter
                        if depth <= miniter:
                            potential_newstart = dfs(t, DFS_CACHE)
                            if potential_newstart.elem_dist < source.elem_dist:
                                # a new starting point
                                del EXPLORED
                                return potential_newstart

                        # try to update the solution if the node element is closer than the last solution
                        if update_solutions(t):
                            updated = True

            roots = new_roots
            # debug messages
            update_msg = "found update" if updated else "no update"
            logger.debug("number of roots at depth {:d} is {:d}, construction time: {:g}, {:s}.".format(depth, len(roots), timer()-t0, update_msg))

        # all done
        del EXPLORED
        return None
Exemplo n.º 2
0
def lat_opt(E1, E2r, **kwargs):
    '''
    find the optimal lattice invarient shear move E1 as close as possible to E2
    allowed kwargs:
     - LG2: special lattice group of E2r
     - nsol: number of solutions
     - dist: choose from "Cauchy", "Ericksen" and "strain", default is "Cauchy"
     - hdlr: logger handler, default is basicConfigure
     - logfile: log file
     - loglevel: log level for the log file
     - disp: display level, 0 - none, 1 - brief, 2 - verbose
     - maxiter: maximum iteration depth, default is 3
     - ihnf: number of the HNF
    '''

    '''
    ===========
    Preparation
    ===========
    '''
    def print_ary(A):
        return "{:s}".format(A.tolist())

    def maxiter_by_nsol(nsol):
        """
        automatically choose maxiter by nsol
        """
        if nsol < 3:
            return 3
        if nsol < 10:
            return 4
        return 5

    # read kwargs
    nsol = kwargs['nsol'] if 'nsol' in kwargs else 1
    global miniter, maxiter
    maxiter = kwargs['maxiter'] if 'maxiter' in kwargs else maxiter_by_nsol(nsol)
    miniter = kwargs['miniter'] if 'miniter' in kwargs else 3
    disp = kwargs['disp'] if 'disp' in kwargs else 1
    if 'logfile' in kwargs:
        logging.basicConfig(level=logging.INFO)
        logger.propagate = False
        logfile = kwargs['logfile']
        fhdlr = logging.FileHandler(logfile, mode='a')
        fhdlr.setLevel(kwargs['loglevel'] if 'loglevel' in kwargs else logging.INFO)
        fhdlr.setFormatter(logging.Formatter(' %(message)s'))
        logger.addHandler(fhdlr)

    if 'ihnf' in kwargs:
        logger.info("Processing the HNF No. {:d}".format(kwargs['ihnf'] + 1))
        if disp > 0:
            print("Processing the HNF No. {:d}".format(kwargs['ihnf'] + 1))

    # get dimension of the problem
    dim = len(E1)
    SLNode.dim = dim

    # start timer
    t_start = timer()

    # LLL-reduction
    Er1 = LLL(E1)
    # starting point
    lo = np.array(np.rint(np.dot(la.inv(E1), Er1)), dtype='int')

    # determine distance function
    distance = kwargs['dist'] if 'dist' in kwargs else 'Cauchy'

    logger.debug("distance is \"{:s}\"".format(distance))

    # distance functions
    if distance == 'Ericksen':
        dist = lambda x: Eric_dist(x, E1, E2r)
    if distance == 'strain':
        E2inv = la.inv(E2r)
        dist = lambda x: strain_dist(x, E1, E2inv)
    if distance == 'Cauchy':
        E2inv = la.inv(E2r)
        dist = lambda x: Cauchy_dist(x, E1, E2inv)

    # lattice groups
    LG1 = Lattice(E1).getspeciallatticegroup().matrices()
    SOLG2 = kwargs['SOLG2'] if 'SOLG2' in kwargs else Lattice(E2r).getspeciallatticegroup().matrices()

    SLNODE_CACHE = {'dist': dist, 'lg1': LG1, 'lg2': SOLG2}
    DFS_CACHE = {'counter': 0}

    '''
    ====================
    Preparation - finish
    ====================
    '''

    '''
    ==============
    Core procedure
    ==============
    '''
    # initialization
    global dopt, lopt, depth
    dopt = []
    lopt = []
    depth = 0

    # DEBUG
    global nlocmin
    nlocmin = 0

    # # look for new minnode
    # minnode_changed = True

    # for each new solution, store all symmetry related l's in memory
    EXT_SOLS = {}

    def ext_sols(l):
        for c in (q1.dot(l).dot(q2) for q1 in LG1 for q2 in SOLG2):
            EXT_SOLS[c.tostring()] = True

    def truncate_sols():
        global dopt, lopt
        # delete last one as long as it is not the same as the supposed tail
        while len(dopt) > nsol and dopt[-1] > dopt[nsol - 1]:
            dopt.pop(-1)
            lopt.pop(-1)

    # update strategy
    def update_solutions(tree):
        global dopt, lopt
        # otherwise, update existing solutions
        if len(dopt) > 0 and tree.elem.tostring() not in EXT_SOLS:
            for i, d in enumerate(dopt):
                if tree.elem_dist <= d:
                    dopt.insert(i, tree.elem_dist)
                    lopt.insert(i, tree.elem)
                    ext_sols(tree.elem)
                    truncate_sols()
                    return True
            if len(dopt) < nsol:
                dopt.append(tree.elem_dist)
                lopt.append(tree.elem)
                ext_sols(tree.elem)
                return True
        # directly append if it is the first one
        elif len(dopt) == 0:
            dopt.append(tree.elem_dist)
            lopt.append(tree.elem)
            ext_sols(tree.elem)
            return True
        return False

    def bfs(source):
        """
        breath first search of solutions
        :return: new starting point or None
        """
        global dopt, lopt, depth, miniter, maxiter

        EXPLORED = {}
        # bfs search from minnode
        EXPLORED[source.elem.tostring()] = True
        roots = [source]
        update_solutions(source)
        logger.debug("loop starts with the first trial {:s} => {:g}".format(str(source.elem.flatten()), source.elem_dist))
        updated = True

        depth = 0
        while depth < miniter or (updated and depth < maxiter):
        # while depth < maxiter:
            # update roots, generator first
            t0 = timer()
            # clear update flag
            updated = False
            # change depth tracker
            depth += 1
            # going down on the tree by iteration
            new_roots = []

            for root in roots:
                for gen, elem in root.children:
                    hashcode = elem.tostring()
                    if not hashcode in EXPLORED:
                        EXPLORED[hashcode] = True
                        t = SLNode(elem, SLNODE_CACHE, parent=gen, grandpa=root.parent)
                        new_roots.append(t)

                        # if in min iter
                        if depth <= miniter:
                            potential_newstart = dfs(t, DFS_CACHE)
                            if potential_newstart.elem_dist < source.elem_dist:
                                # a new starting point
                                del EXPLORED
                                return potential_newstart

                        # try to update the solution if the node element is closer than the last solution
                        if update_solutions(t):
                            updated = True

            roots = new_roots
            # debug messages
            update_msg = "found update" if updated else "no update"
            logger.debug("number of roots at depth {:d} is {:d}, construction time: {:g}, {:s}.".format(depth, len(roots), timer()-t0, update_msg))

        # all done
        del EXPLORED
        return None

    logger.debug("starting point: {:s}".format(str(lo.flatten())))
    new_start = SLNode(lo, SLNODE_CACHE)
    nrestart = 0

    while new_start is not None:
        # local minimization
        minnode = dfs(new_start, DFS_CACHE)
        # DEBUG
        nrestart += 1
        # BFS
        new_start = bfs(minnode)

    DFS_CACHE = {'counter': 0}
    SLNODE_CACHE = {'dist': dist, 'lg1': LG1, 'lg2': SOLG2}

    logger.debug("restarted {:d} times in total".format(nrestart))

    # if depth == maxiter and updated:
    #     lprint("DEBUG: maximum depth {:d} reached before solutions guaranteed".format(maxiter), 2)

    '''
    =======================
    Core procedure - finish
    =======================
    '''
    # finish timer
    t_elapsed = timer() - t_start

    if 'ihnf' in kwargs:
        logger.debug("HNF No. {:d} finished.".format(kwargs['ihnf']+1))
    for d, l in zip(dopt, lopt):
        logger.debug("{:.4f}, {:s}".format(d, str(l.flatten())))

    final_msg = "{:d} / {:d} solution(s) found by {:d} iterations and in {:g} sec.".format(len(dopt), nsol, depth, t_elapsed)
    if 'ihnf' in kwargs:
        final_msg = "HNF No. {:d}: ".format(kwargs['ihnf']+1) + final_msg
    logger.info(final_msg)
    if disp > 1:
        print(final_msg)
    return {'lopt': lopt, 'dopt': dopt}
Exemplo n.º 3
0
def lat_opt(E1, E2r, **kwargs):
    '''
    find the optimal lattice invarient shear move E1 as close as possible to E2
    allowed kwargs:
     - LG2: special lattice group of E2r
     - nsol: number of solutions
     - dist: choose from "Cauchy", "Ericksen" and "strain", default is "Cauchy"
     - hdlr: logger handler, default is basicConfigure
     - logfile: log file
     - loglevel: log level for the log file
     - disp: display level, 0 - none, 1 - brief, 2 - verbose
     - maxiter: maximum iteration depth, default is 3
     - ihnf: number of the HNF
    '''
    '''
    ===========
    Preparation
    ===========
    '''
    def print_ary(A):
        return "{:s}".format(A.tolist())

    def maxiter_by_nsol(nsol):
        """
        automatically choose maxiter by nsol
        """
        if nsol < 3:
            return 3
        if nsol < 10:
            return 4
        return 5

    # read kwargs
    nsol = kwargs['nsol'] if 'nsol' in kwargs else 1
    global miniter, maxiter
    maxiter = kwargs['maxiter'] if 'maxiter' in kwargs else maxiter_by_nsol(
        nsol)
    miniter = kwargs['miniter'] if 'miniter' in kwargs else 3
    disp = kwargs['disp'] if 'disp' in kwargs else 1
    if 'logfile' in kwargs:
        logging.basicConfig(level=logging.INFO)
        logger.propagate = False
        logfile = kwargs['logfile']
        fhdlr = logging.FileHandler(logfile, mode='a')
        fhdlr.setLevel(kwargs['loglevel'] if 'loglevel' in
                       kwargs else logging.INFO)
        fhdlr.setFormatter(logging.Formatter(' %(message)s'))
        logger.addHandler(fhdlr)

    if 'ihnf' in kwargs:
        logger.info("Processing the HNF No. {:d}".format(kwargs['ihnf'] + 1))
        if disp > 0:
            print("Processing the HNF No. {:d}".format(kwargs['ihnf'] + 1))

    # get dimension of the problem
    dim = len(E1)
    SLNode.dim = dim

    # start timer
    t_start = timer()

    # LLL-reduction
    Er1 = LLL(E1)
    # starting point
    lo = np.array(np.rint(np.dot(la.inv(E1), Er1)), dtype='int')

    # determine distance function
    distance = kwargs['dist'] if 'dist' in kwargs else 'Cauchy'

    logger.debug("distance is \"{:s}\"".format(distance))

    # distance functions
    if distance == 'Ericksen':
        dist = lambda x: Eric_dist(x, E1, E2r)
    if distance == 'strain':
        E2inv = la.inv(E2r)
        dist = lambda x: strain_dist(x, E1, E2inv)
    if distance == 'Cauchy':
        E2inv = la.inv(E2r)
        dist = lambda x: Cauchy_dist(x, E1, E2inv)

    # lattice groups
    LG1 = Lattice(E1).getspeciallatticegroup().matrices()
    SOLG2 = kwargs['SOLG2'] if 'SOLG2' in kwargs else Lattice(
        E2r).getspeciallatticegroup().matrices()

    SLNODE_CACHE = {'dist': dist, 'lg1': LG1, 'lg2': SOLG2}
    DFS_CACHE = {'counter': 0}
    '''
    ====================
    Preparation - finish
    ====================
    '''
    '''
    ==============
    Core procedure
    ==============
    '''
    # initialization
    global dopt, lopt, depth
    dopt = []
    lopt = []
    depth = 0

    # DEBUG
    global nlocmin
    nlocmin = 0

    # # look for new minnode
    # minnode_changed = True

    # for each new solution, store all symmetry related l's in memory
    EXT_SOLS = {}

    def ext_sols(l):
        for c in (q1.dot(l).dot(q2) for q1 in LG1 for q2 in SOLG2):
            EXT_SOLS[c.tostring()] = True

    def truncate_sols():
        global dopt, lopt
        # delete last one as long as it is not the same as the supposed tail
        while len(dopt) > nsol and dopt[-1] > dopt[nsol - 1]:
            dopt.pop(-1)
            lopt.pop(-1)

    # update strategy
    def update_solutions(tree):
        global dopt, lopt
        # otherwise, update existing solutions
        if len(dopt) > 0 and tree.elem.tostring() not in EXT_SOLS:
            for i, d in enumerate(dopt):
                if tree.elem_dist <= d:
                    dopt.insert(i, tree.elem_dist)
                    lopt.insert(i, tree.elem)
                    ext_sols(tree.elem)
                    truncate_sols()
                    return True
            if len(dopt) < nsol:
                dopt.append(tree.elem_dist)
                lopt.append(tree.elem)
                ext_sols(tree.elem)
                return True
        # directly append if it is the first one
        elif len(dopt) == 0:
            dopt.append(tree.elem_dist)
            lopt.append(tree.elem)
            ext_sols(tree.elem)
            return True
        return False

    def bfs(source):
        """
        breath first search of solutions
        :return: new starting point or None
        """
        global dopt, lopt, depth, miniter, maxiter

        EXPLORED = {}
        # bfs search from minnode
        EXPLORED[source.elem.tostring()] = True
        roots = [source]
        update_solutions(source)
        logger.debug("loop starts with the first trial {:s} => {:g}".format(
            str(source.elem.flatten()), source.elem_dist))
        updated = True

        depth = 0
        while depth < miniter or (updated and depth < maxiter):
            # while depth < maxiter:
            # update roots, generator first
            t0 = timer()
            # clear update flag
            updated = False
            # change depth tracker
            depth += 1
            # going down on the tree by iteration
            new_roots = []

            for root in roots:
                for gen, elem in root.children:
                    hashcode = elem.tostring()
                    if not hashcode in EXPLORED:
                        EXPLORED[hashcode] = True
                        t = SLNode(elem,
                                   SLNODE_CACHE,
                                   parent=gen,
                                   grandpa=root.parent)
                        new_roots.append(t)

                        # if in min iter
                        if depth <= miniter:
                            potential_newstart = dfs(t, DFS_CACHE)
                            if potential_newstart.elem_dist < source.elem_dist:
                                # a new starting point
                                del EXPLORED
                                return potential_newstart

                        # try to update the solution if the node element is closer than the last solution
                        if update_solutions(t):
                            updated = True

            roots = new_roots
            # debug messages
            update_msg = "found update" if updated else "no update"
            logger.debug(
                "number of roots at depth {:d} is {:d}, construction time: {:g}, {:s}."
                .format(depth, len(roots),
                        timer() - t0, update_msg))

        # all done
        del EXPLORED
        return None

    logger.debug("starting point: {:s}".format(str(lo.flatten())))
    new_start = SLNode(lo, SLNODE_CACHE)
    nrestart = 0

    while new_start is not None:
        # local minimization
        minnode = dfs(new_start, DFS_CACHE)
        # DEBUG
        nrestart += 1
        # BFS
        new_start = bfs(minnode)

    DFS_CACHE = {'counter': 0}
    SLNODE_CACHE = {'dist': dist, 'lg1': LG1, 'lg2': SOLG2}

    logger.debug("restarted {:d} times in total".format(nrestart))

    # if depth == maxiter and updated:
    #     lprint("DEBUG: maximum depth {:d} reached before solutions guaranteed".format(maxiter), 2)
    '''
    =======================
    Core procedure - finish
    =======================
    '''
    # finish timer
    t_elapsed = timer() - t_start

    if 'ihnf' in kwargs:
        logger.debug("HNF No. {:d} finished.".format(kwargs['ihnf'] + 1))
    for d, l in zip(dopt, lopt):
        logger.debug("{:.4f}, {:s}".format(d, str(l.flatten())))

    final_msg = "{:d} / {:d} solution(s) found by {:d} iterations and in {:g} sec.".format(
        len(dopt), nsol, depth, t_elapsed)
    if 'ihnf' in kwargs:
        final_msg = "HNF No. {:d}: ".format(kwargs['ihnf'] + 1) + final_msg
    logger.info(final_msg)
    if disp > 1:
        print(final_msg)
    return {'lopt': lopt, 'dopt': dopt}
Exemplo n.º 4
0
    def bfs(source):
        """
        breath first search of solutions
        :return: new starting point or None
        """
        global dopt, lopt, depth, miniter, maxiter

        EXPLORED = {}
        # bfs search from minnode
        EXPLORED[source.elem.tostring()] = True
        roots = [source]
        update_solutions(source)
        logger.debug("loop starts with the first trial {:s} => {:g}".format(
            str(source.elem.flatten()), source.elem_dist))
        updated = True

        depth = 0
        while depth < miniter or (updated and depth < maxiter):
            # while depth < maxiter:
            # update roots, generator first
            t0 = timer()
            # clear update flag
            updated = False
            # change depth tracker
            depth += 1
            # going down on the tree by iteration
            new_roots = []

            for root in roots:
                for gen, elem in root.children:
                    hashcode = elem.tostring()
                    if not hashcode in EXPLORED:
                        EXPLORED[hashcode] = True
                        t = SLNode(elem,
                                   SLNODE_CACHE,
                                   parent=gen,
                                   grandpa=root.parent)
                        new_roots.append(t)

                        # if in min iter
                        if depth <= miniter:
                            potential_newstart = dfs(t, DFS_CACHE)
                            if potential_newstart.elem_dist < source.elem_dist:
                                # a new starting point
                                del EXPLORED
                                return potential_newstart

                        # try to update the solution if the node element is closer than the last solution
                        if update_solutions(t):
                            updated = True

            roots = new_roots
            # debug messages
            update_msg = "found update" if updated else "no update"
            logger.debug(
                "number of roots at depth {:d} is {:d}, construction time: {:g}, {:s}."
                .format(depth, len(roots),
                        timer() - t0, update_msg))

        # all done
        del EXPLORED
        return None