Exemplo n.º 1
0
    def get_heuristics(cls, graph, processes):
        # get number of nodes
        n = len(list(graph.get_nodes()))

        # STOP THE USER IF THERE ARE TOO MANY NODES
        if n > 800:
            # use landmark heuristic method
            MyLogger.add_message("GENERATING LANDMARKS", __name__, "INFO")

            return cls.gen_landmark_heuristic(graph, processes=processes)
        else:
            # find all pairs shortest distance
            MyLogger.add_message("ALL PAIRS SHORTEST DISTANCE", __name__, "INFO")
            return cls.gen_all_pairs_shortest_dist(graph, processes=processes)
Exemplo n.º 2
0
    def nominate(self):
        """In this function, a node is nominated from the open set, which essentially updates the open set.
        
        `nominate` is done using a priority queue. A flag is used in the conditional to 
        ensure the function is not called more than once prior to an update.

        Returns:
            True: if a node was nominated

        """
        frontier = self.frontier
        parent = self.parent
        g = self.g

        # NOTE Probably dont need this ""'nominated' ensures this function doesn't get called multiple times before update"
        if not frontier.empty():
            # current node is immediately in the closed list
            currentP, current = frontier.get_test(
            )  # update current to be the item with best priority
            self.current = current
            self.currentF = self.f[current]
            self.currentP = currentP

            # LOG nomination
            MyLogger.add_message(
                "{} nominated {} with priority {}".format(
                    self.id, self.current, self.currentP), __name__, "DEBUG")

            #print("Terminal, current: ",self.start, current)
            if self.visualize:
                # Update plot with visuals
                # self.animateCurrent.update_clean(current)
                AnimateV2.add_line("nominated_{}".format(self.id),
                                   current[0],
                                   current[1],
                                   'ko',
                                   zorder=15,
                                   draw_clean=True,
                                   markersize=10)
                # AnimateV2.update()
            # #Early exit if we reached our goal
            # if current == self.goal:
            #     return parent, g, current

            # return true if nominated
            return True

        # if no nomination
        return False
Exemplo n.º 3
0
    def nominate(self):
        """Each component nominates a node from its open set
               
        For each component in `comps`, we nominate a single node. 
        If the nominate function returns a `True`, we move on to
        storing the nominated node and its priority value (Fcost)
        in a queue variable called nodeQueue.
        
        """
        MyLogger.add_message("performing nominate() ", __name__, "INFO")

        for ndx, c in self.comps.items():
            if ndx not in self.nodeQueue.elements:
                if c.nominate():
                    self.nodeQueue.put(ndx, c.currentP)
Exemplo n.º 4
0
    def tree_update(self):
        """override tree_update because we need cycle detection and no merging """
        MyLogger.add_message("performing tree_update() ", __name__, "INFO")

        # Empty path queue, gather up the solutions
        sol = Common.solution_handler(comps=self.comps, path_queue=self.pathQueue, cycle_detector=self.cd, \
            terminals=self.terminals, criteria=self.path_queue_criteria, merging=False)

        # add paths to solution set
        for s in sol:
            # self.add_solution(s['path'], s['dist'], s['terms'])
            # t1,t2 = s['terms']
            # if t1 in self.comps and t2 in self.comps:
            # print(s)

            t1, t2 = s['components']
            pdist = s['dist']

            # Get common node between two components
            dist, commonNode = self.UFeasPath[t1][t2]

            path, _, term_actual = Common.get_path(comps=self.comps, sel_node=commonNode, term_edge=(t1,t2),\
                reconstruct_path_func = reconstruct_path)

            if abs(pdist - dist) > 0.1 or abs(pdist - _) > 0.1:
                # print("")
                raise ValueError(
                    "distances don't match! path queue and feasible table is conflicting!",
                    self.terminals, self, pdist, dist)

            Common.add_solution(path=path, dist=dist, edge=term_actual,\
                solution_set=self.S, terminals=self.terminals)

            # True as soon as we add a solution
            self.FLAG_STATUS_pathConverged = True

            # Perform merging
            # Common.merge_comps(self.comps, term_edge=(t1,t2), nodeQueue=self.nodeQueue, cache=self.F)

            # TODO find a better way to animate path
            if cfg.Animation.visualize:
                # self.animateS.update_clean(np.vstack(self.S['path']).T.tolist())
                AnimateV2.add_line("solution",
                                   np.vstack(self.S['path']).T.tolist(),
                                   'ro',
                                   markersize=10,
                                   zorder=10,
                                   alpha=0.5)
Exemplo n.º 5
0
    def run(self, strategy, **kwargs):
        """Query an algorithm based on the user's selection

        Args:
            strategy (str): user's desired algorithm to run
        
        Returns:
            True: When no errors

        Raises:
            AssertionError: if strategy doesn't exist or has not been implemented yet

        """
        # Update strategy to most recent run
        self.strategy = strategy

        # run based on input
        if self.strategy == "S*-unmerged":
            self.instances[self.strategy] = Unmerged(self._graph,
                                                     self._terminals)
        elif self.strategy == "S*-HS":
            self.instances[self.strategy] = SstarHS(self._graph,
                                                    self._terminals)
        elif self.strategy == "S*-HS0":
            self.instances[self.strategy] = SstarHS0(self._graph,
                                                     self._terminals)
        elif self.strategy == "Kruskal":
            self.instances[self.strategy] = Kruskal(self._graph,
                                                    self._terminals)
        elif self.strategy == "S*-BS":
            self.instances[self.strategy] = SstarBS(self._graph,
                                                    self._terminals)
        elif self.strategy == "S*-MM":
            self.instances[self.strategy] = SstarMM(self._graph,
                                                    self._terminals)
        elif self.strategy == "S*-MM0":
            self.instances[self.strategy] = SstarMM0(self._graph,
                                                     self._terminals)
        else:
            raise ValueError("Strategy {} is not found".format(self.strategy))
        # Log run
        MyLogger.add_message("Running: " + self.strategy, __name__, "INFO")
        self.instances[self.strategy].run_algorithm(**kwargs)
        # Return True when no errors
        return True
Exemplo n.º 6
0
    def test_create_logger(self):
        # TESTING PURPOSE ONLY: redirect out,err to capturing objects
        old_out, old_err = sys.stdout, sys.stderr
        new_out, new_err = io.StringIO(), io.StringIO()
        sys.stdout, sys.stderr = new_out, new_err 

        # Configure console level to following choices before importing Logger:
        cfg.Misc.console_level = "DEBUG"
        from steinerpy.library.logger import MyLogger

        loggerName = "test_logger"

        msgDebug = "This is a debug level"
        input_level = "debug"
        MyLogger.add_message(msgDebug, loggerName, input_level)

        msgInfo = "This is info level"
        input_level = "info"
        MyLogger.add_message(msgInfo, loggerName, input_level)

        msgWarn = "This is warning level"
        input_level = "warning"
        MyLogger.add_message(msgWarn, loggerName, input_level)
        
        msgErr = "This is error level"
        input_level = "error"
        MyLogger.add_message(msgErr, loggerName, input_level)

        msgCrit = "This is critical level"
        input_level = "critical"
        MyLogger.add_message(msgCrit, loggerName, input_level)    

        # TESTING PURPOSE ONLY: redirect out, err back to terminal stream
        sys.stdout, sys.stderr = old_out, old_err 

        # # print captured data
        # print(new_err.getvalue().strip(), new_out.getvalue())

        self.assertTrue("- test_logger - DEBUG - \x1b[92mThis is a debug level\x1b[0m" in new_err.getvalue())
        self.assertTrue("- test_logger - INFO - \x1b[94mThis is info level\x1b[0m" in new_err.getvalue())
        self.assertTrue("- test_logger - WARNING - \x1b[93mThis is warning level\x1b[0m" in new_err.getvalue())
        self.assertTrue("- test_logger - ERROR - \x1b[91mThis is error level\x1b[0m" in new_err.getvalue())
        self.assertTrue("- test_logger - CRITICAL - \x1b[31mThis is critical level\x1b[0m" in new_err.getvalue())
Exemplo n.º 7
0
    def run_func(self):
        """ Queries Kruskal's algorithm for each run instance for baseline results

        Results are saved to file

        Raises:
            FileExistsError: If output (baseline) file already exists
        
        Todo:
            * Consider using timeit for more accurate results
                https://stackoverflow.com/questions/7370801/how-to-measure-elapsed-time-in-python

        """
        #start time
        t0 = timer()

        # Get path and check for existance. Don't overwrite files!
        # directory = os.path.dirname(os.path.realpath(__file__))
        if os.path.exists(os.path.join(self.save_directory, self.filename)):
            raise FileExistsError('{} already exists!'.format(self.filename))

        # Create n-terminals for m-instances
        if self._terminals is None:
            # self._terminals = [[list(i) for i in self.create_terminals()][0] for j in range(self.m_instances)]
            self._terminals = self.create_terminals()
        obstacles = self.graph.obstacles

        # Run Kruskals on each and save results
        solution = []
        for ndx, t in enumerate(self._terminals):
            MyLogger.add_message("Running terminals: {}".format(t), __name__,
                                 "INFO")

            # ko = Kruskal(self.graph, t)
            # ko.run_algorithm()
            # solution.append(ko.return_solutions())
            # Ensure every terminal is reachable!
            keepRunning = True
            while keepRunning:
                try:
                    context = Context(self.graph, t)
                    context.run('Kruskal')
                    solution.append(context.return_solutions())
                    keepRunning = False
                except:
                    MyLogger.add_message(
                        "one or more terminals is landlocked! In ndx {}, with terminals: {}"
                        .format(ndx, t), __name__, "CRITICAL")
                    # Generate new terminals and replace the ones we have right now
                    t = [list(i) for i in self.create_terminals()][0]
                    self._terminals[ndx] = t

            print(ndx, '\n')

        # end time
        t1 = timer() - t0

        # dump instances and solution to file
        # directory = os.getcwd()
        if self.save_to_file:
            with open(os.path.join(self.save_directory, self.filename),
                      'wb') as f:
                pickle.dump(
                    {
                        'terminals': self._terminals,
                        'solution': solution,
                        'obstacles': obstacles
                    }, f)

        print("Finished! Wrote baseline file! Now generate results! {}".format(
            t1))
        if cfg.Misc.sound_alert == True:
            os.system(
                'spd-say "Finished! Wrote baseline file! Now generate results!"'
            )

        return {
            'terminals': self._terminals,
            'solution': solution,
            'obstacles': obstacles
        }
Exemplo n.º 8
0
    def update(self):
        """The open/closed list is updated here, and the open list is expanded with neighboring nodes

        For each neighbor of the nominated node (denoted as `current`), we identify its gcost,
        parent node, and priority. These 3 items are stored into 3 separate dictionaries.

        """
        frontier = self.frontier
        parent = self.parent
        g = self.g
        # current = self.current
        # frontier.delete(current)
        priority, current = frontier.get()

        # Update gmin,rmin,fmin heaps
        self.gmin_heap.delete(current)
        self.rmin_heap.delete(current)
        self.fmin_heap.delete(current)

        # self.closedList[current] = currentP
        # Delete current node from frontier

        #expand current node and check neighbors
        # Update stats logging
        GenericSearch.update_expanded_nodes()
        # visualize the recently closed node
        if self.visualize:
            # self.animateClosed.update(current)

            # Delete nominated node drawing, add it as closed

            AnimateV2.add_line("closed_{}".format(self.id),
                               current[0],
                               current[1],
                               'mo',
                               markersize=10)
            # AnimateV2.update()

            # hide the nominate node temporarily
            AnimateV2.add_line("nominated_{}".format(self.id),
                               current[0],
                               current[1],
                               'ko',
                               alpha=0,
                               zorder=15,
                               draw_clean=True,
                               markersize=10)

            # Show recently closed node with a white x (the best nominated node over all)
            # AnimateV2.add_line("recent_closed_{}".format(self.id), current[0], current[1], 'wx', alpha=1, zorder=16, draw_clean=True, markersize=10)
            # AnimateV2.update()

        # refresh neighbors
        self.currentNeighs = []

        # Add new nodes to frontier
        for next in self.graph.neighbors(current):
            g_next = g[current] + self.graph.cost(current, next)

            # if next location not in CLOSED LIST or its cost is less than before
            if next not in g or g_next < g[next]:
                # Store neighbor's gcost
                g[next] = g_next

                # Calculate priority and time it
                # Call priority function to get next node's priority (TODO: rename fcosts -> priority!)
                start = timer()
                priority = self.fCosts(self, g, next)
                end = timer()
                MyTimer.add_time("fcosts_time", end - start)

                # Update frontier and parent list
                frontier.put(next, priority)
                parent[next] = current

                # update gmin,rmin, fmin heaps
                self.gmin_heap.put(next, g_next)
                self.rmin_heap.put(next, g[current])
                self.fmin_heap.put(next, self.f[next])

                # track current neighbors
                self.currentNeighs.append(next)

            if self.visualize:
                # self.animateNeighbors.update(next)
                # Add neighbors
                x = []
                y = []
                for n in self.frontier.elements:
                    x.append(n[0])
                    y.append(n[1])
                AnimateV2.add_line("neighbors_{}".format(self.id),
                                   x,
                                   y,
                                   'cD',
                                   markersize=7,
                                   draw_clean=True)
                # Hide the best nominated node now
                # AnimateV2.add_line("recent_closed_{}".format(self.id), current[0], current[1], 'wx', alpha=0, draw_clean=True, markersize=10)

        # if self.visualize:
        #     AnimateV2.update()

        # consider deleting fvalues to save memory, since it's only relevant to openset
        del self.f[current]

        self.nominated = False
        MyLogger.add_message("{} updated!".format(self.id), __name__, "DEBUG")
Exemplo n.º 9
0
    def run_func(self, algorithms_to_run=None):
        """Run each of our algorithms using a context class

        The context class takes a 'graph' and an instance of 't' terminals, 
        then executes each algorithm on a given instance.
        Results are appended to the appropriate location in the 'results' attribute.
        Finally, output files have a  'results_' string prepended in the current directory
        for processing

        Returns:
            results (dict): Contains results for the algorithms ran

        """
        t0 = timer()
        # Use contextualizer to run algorithms (make sure debug visualizer is off for speed)
        if algorithms_to_run is None:
            results = {'S*-HS':[], 'S*-BS': [], 'S*-unmerged':[],  \
                    'S*-MM':[]}
            algorithms_to_run = list(results)
        else:
            results = {alg: [] for alg in algorithms_to_run}

        # results = {'Astar':[], 'SstarAstar':[], 'SstarDijkstra': [], 'SstarPrimalDual': []}

        for ndx, t in enumerate(self.data['terminals']):

            if self.run_instance is not None and ndx != self.run_instance:
                # Skip everything except user-specified run
                continue

            # Add obstacles to graph?
            if 'obstacles' in self.data and self.data[
                    'obstacles'] and self.graph.obstacles is not None:
                # self.graph.set_obstacles(self.data['obstacles'][ndx])
                self.graph.set_obstacles(self.data['obstacles'])

            # Log terminals
            MyLogger.add_message("terminals: {}".format(t), __name__, "INFO")

            # Create context
            context = Context(self.graph, t)

            # context.run('Astar')
            # results['Astar'].append(context.return_solutions())

            for alg in algorithms_to_run:
                context.run(alg)
                results[alg].append(context.return_solutions())
            # context.run('SstarMM')
            # results['SstarMM'].append(context.return_solutions())

            # context.run('SstarAstar')
            # results['SstarAstar'].append(context.return_solutions())

            # context.run('SstarPrimalDual')
            # results['SstarPrimalDual'].append(context.return_solutions())

            #We already have kruskals as base line
            print('finished with instance {} \n'.format(ndx))

        t1 = timer() - t0

        # Save results!
        if self.save_to_file:
            # directory = os.path.dirname(os.path.realpath(__file__))
            with open(os.path.join(self.save_directory, self.results_filename),
                      'wb') as f:
                pickle.dump(results, f)

            print(
                "Wrote results to file! Now process them! {} secs".format(t1))
            if cfg.Misc.sound_alert is True:
                os.system(
                    'spd-say "Finished! Wrote results to file! Now process them!"'
                )

        return results
Exemplo n.º 10
0
    def update(self):
        """Update the component pertaining to the least fcost-valued node
        
        Among all nominated nodes, choose the one with least f cost using a priority queue

        """
        # Get best ndx from priority queue
        # print(self.run_debug)
        MyLogger.add_message("performing update() ", __name__, "INFO")

        # if not self.nodeQueue.empty():
        best_priority, best_ndx = self.nodeQueue.get()

        # get best component object, and g cost of best node
        bestC = self.comps[best_ndx]
        bestCurrent = bestC.current
        bestGVal = bestC.g[bestCurrent]
        bestFVal = bestC.currentF

        # Get parent (if possible)
        bestParent = bestC.parent.get(bestCurrent, None)

        # Store and return the selected node(s)
        self.selNode = bestCurrent
        self.selData = {}
        self.selData.update({
            'to': bestParent,
            'terminalInd': best_ndx,
            'gcost': bestGVal,
            'fcost': bestFVal,
            'status': 'closed'
        })

        MyLogger.add_message(
            "updated {} with node {}".format(best_ndx, bestCurrent), __name__,
            "Debug")

        ##############################
        ### NEW FUNCTIONALITY HERE ###
        ##############################
        # destination list
        # dest = self.comps[self.selData['terminalInd']].goal.values()

        # # Check for complete path
        # self.iscollided = Common.set_collision_check(sel_node=self.selNode, sel_data=self.selData,\
        #     target_list=dest, cache=self.F)

        # if self.iscollided:
        #     # Define terminal indices
        #     t1 = self.selData['terminalInd']
        #     t2 = (self.terminals.index(self.selNode), )

        #     MyLogger.add_message("goals(PRE) of {} is {}".format(t1, self.comps[t1].goal), __name__, "Debug")
        #     MyLogger.add_message("goals(PRE) of {} is {}".format(t2, self.comps[t2].goal), __name__, "Debug")

        #     # update destination list TEST THIS
        #     del self.comps[t1].goal[t2[0]]
        #     del self.comps[t2].goal[t1[0]]

        #     # reprioritize
        #     if self.comps[t2].goal:
        #         self.comps[t2].reprioritize()

        #     MyLogger.add_message("goals(POST) of {} is {}".format(t1, self.comps[t1].goal), __name__, "Debug")
        #     MyLogger.add_message("goals(POST) of {} is {}".format(t2, self.comps[t2].goal), __name__, "Debug")

        ###############################
        ###  END NEW FUNCTIONALITY  ###
        ###############################

        # Now update the closed/open list for the 'best' component
        # make sure goals are not empty!
        # if self.comps[best_ndx].goal:
        bestC.update()
Exemplo n.º 11
0
    def path_check(self):
        """  
        1. Check for set collision
        2. Find shortest path among two colliding sets

        """
        # MyLogger.add_message("performing path_check() ", __name__, "INFO")
        # t1 = self.selData['terminalInd']
        # updatedComp = self.comps[t1]
        # for t2, c in self.comps.items():
        #     # skip if trying to intersect with itself
        #     if t1 == t2:
        #         continue

        #     updateSet = set(updatedComp.current)
        #     # # Add updated component recent open and closed list
        #     # updateSet = set(updatedComp.currentNeighs)
        #     # # updateSet = set(updatedComp.frontier.elements)
        #     # updateSet.add(updatedComp.current)
        #     # if c.currentNeighs:
        #     #     updateSet.union(set(c.currentNeighs))
        #     # updateSet.add(c.current)

        #     UFeas = None
        #     for k in updateSet:
        #         if k in self.comps[t1].goal.values():
        #             candU = self.comps[t1].g[k]
        #             if  UFeas is None or candU < UFeas:
        #                 UFeas = candU
        #                 commonNode = k

        #     # if UFeas is not None:
        #     #     updateSet = set(updatedComp.frontier.elements)
        #     #     for k in updateSet:
        #     #         if k in self.comps[t1].g and k in self.comps[t2].g:
        #     #             candU = self.comps[t1].g[k] + self.comps[t2].g[k]
        #     #             if  UFeas is None or candU < UFeas:
        #     #                 UFeas = candU
        #     #                 commonNode = k

        #     ######################################################################################
        #     # try:
        #     #     jointSet = ((c.g[k] + updatedComp.g[k], k) for k in set(c.g).intersection(set(updatedComp.frontier.elements)))
        #     #     minItem = min(jointSet)
        #     #     # UFeas = minItem[0]
        #     #     # commonNode = minItem[1]
        #     #     del jointSet
        #     # except:
        #     #     # UFeas, commonNode = None, None
        #     #     pass
        #     ######################################################################################
        #     # if abs(UFeas - 11.656) < 1e-3:
        #     #     self.testone.append(UFeas)

        UFeas = None
        t1 = self.selData['terminalInd']
        k = self.comps[t1].current
        if k in self.comps[t1].goal.values():
            t2 = (self.terminals.index(k), )
            if (t1, t2) not in self.pathQueue.entry_table or (
                    t2, t1) not in self.pathQueue.entry_table:
                UFeas = self.comps[t1].g[k]
                commonNode = k

                # set lmins for each comp
                # self.comps[t1].lmin = UFeas
                # self.comps[t2].lmin = UFeas

                #update feasible path table
                if t1 in self.UFeasPath:
                    self.UFeasPath[t1].update({t2: [UFeas, commonNode]})
                else:
                    self.UFeasPath.update({t1: {t2: [UFeas, commonNode]}})

                if t2 in self.UFeasPath:
                    self.UFeasPath[t2].update({t1: [UFeas, commonNode]})
                else:
                    self.UFeasPath.update({t2: {t1: [UFeas, commonNode]}})

        if UFeas is not None:
            # set lmins for each component
            if UFeas < self.comps[t1].lmin or self.comps[t1].lmin == 0:
                self.comps[t1].lmin = UFeas
            if UFeas < self.comps[t2].lmin or self.comps[t2].lmin == 0:
                self.comps[t2].lmin = UFeas
            # Subtract some slack due to numerical issues
            # t1, t2 = t1feas, t2feas
            sp = self.shortest_path_check(comps=self.comps,
                                          term_edge=(t1, t2),
                                          bestVal=UFeas)
            if sp:
                # if criteria is satisfied, update the path queue
                # Get path based on sel_node

                # path, dist, term_actual = Common.get_path(comps=self.comps, sel_node=commonNode, term_edge=(t1,t2),\
                #     reconstruct_path_func = reconstruct_path)

                ###########################################
                ### # update destination list TEST THIS ###
                ###########################################
                # MyLogger.add_message("goals(PRE) of {} is {}".format(t1, self.comps[t1].goal), __name__, "Debug")
                # MyLogger.add_message("goals(PRE) of {} is {}".format(t2, self.comps[t2].goal), __name__, "Debug")

                # update destination list TEST THIS
                del self.comps[t1].goal[t2[0]]
                del self.comps[t2].goal[t1[0]]

                # reprioritize
                if self.comps[t2].goal:
                    self.comps[t2].reprioritize()
                if self.comps[t1].goal:
                    self.comps[t1].reprioritize()
                # # for ndx, c in self.comps.items(): print(ndx, c.fmin_heap.pq, "\n")

                # Delete respective components from nodeQueue
                if t1 in self.nodeQueue.elements:
                    self.nodeQueue.delete(t1)
                if t2 in self.nodeQueue.elements:
                    self.nodeQueue.delete(t2)

                # MyLogger.add_message("goals(POST) of {} is {}".format(t1, self.comps[t1].goal), __name__, "Debug")
                # MyLogger.add_message("goals(POST) of {} is {}".format(t2, self.comps[t2].goal), __name__, "Debug")

                ############################################
                # ## End update destination list and rep ###
                # ##########################################

                MyLogger.add_message(
                    "paths in solution set: {}".format(len(self.S['dist'])),
                    __name__, "INFO")

                # # Set another lower bound on components due to declared shortest path
                # if dist > 0 and dist < self.comps[t1].lmin or self.comps[t1].lmin == 0:
                #     self.comps[t1].lmin = dist

                # if dist > 0 and dist < self.comps[t2].lmin or self.comps[t2].lmin == 0:
                #     self.comps[t2].lmin = dist

                # self.comps[t1].lmin = dist
                # self.comps[t2].lmin = dist

                # self.pathQueue.put({'terms': (t1,t2), 'term_actual': term_actual, 'path':path, 'dist':dist}, dist)
                # self.pathQueue.put( ((t1,t2), term_actual, tuple(path), dist), dist)
                self.pathQueue.put((t1, t2), UFeas)

                MyLogger.add_message("Added path to pathQueue", __name__,
                                     "DEBUG")

                MyLogger.add_message(
                    "pathQueue len now: {}".format(len(
                        self.pathQueue.elements)), __name__, "INFO")

                if cfg.Misc.console_level == "DEBUG":
                    self.debug_fmin()
                    self.debug_gmin()
                    self.debug_pmin()
                    # self.debug_lmin()
                    self.debug_rmin()
                    testtesttest = 1