Esempio n. 1
0
File: ef.py Progetto: Pyomo/pyomo
    def _solve_impl(self,
                    sp,
                    output_solver_log=False,
                    keep_solver_files=False,
                    symbolic_solver_labels=False):
        """
        Solve a stochastic program by building the extensive
        form and calling and a Pyomo solver.

        See the 'solve' method on the base class for
        additional keyword documentation.

        Args:
            sp: The stochastic program to solve. Pyro based
                managers are not accepted. All scenario
                models must be managed locally.
            output_solver_log (bool): Stream the solver
                output during the solve.
            keep_solver_files (bool): Retain temporary solver
                input and output files after the solve completes.
            symbolic_solver_labels (bool): Generate solver
                input files using human-readable symbols
                (makes debugging easier).

        Returns: A results object with information about the solution.
        """

        if isinstance(sp,
                      (ScenarioTreeManagerClientPyro,
                       ScenarioTreeManagerSolverClientPyro)):
            raise TypeError("The EF solver does not handle "
                            "Pyro-based scenario tree managers")

        orig_parents = {}
        if sp.scenario_tree.contains_bundles():
            for scenario in sp.scenario_tree.scenarios:
                if scenario._instance._parent is not None:
                    orig_parents[scenario] = scenario._instance._parent
                    scenario._instance._parent = None
                    assert not scenario._instance_objective.active
                    scenario._instance_objective.activate()
        try:
            with ExtensiveFormAlgorithm(sp, self._options) as ef:
                ef.build_ef()
                ef.solve(
                    output_solver_log=output_solver_log,
                    keep_solver_files=keep_solver_files,
                    symbolic_solver_labels=symbolic_solver_labels,
                    check_status=False)
        finally:
            for scenario, parent in orig_parents.items():
                scenario._instance._parent = parent
                assert scenario._instance_objective.active
                scenario._instance_objective.deactivate()

        results = SPSolverResults()
        results.objective = ef.objective
        results.bound = ef.bound
        results.status = ef.solution_status
        results.solver.status = ef.solver_status
        results.solver.termination_condition = ef.termination_condition
        results.solver.message = ef.termination_message
        results.solver.time = ef.time
        results.solver.pyomo_time = ef.pyomo_time
        results.xhat = None
        if ef.solution_status is not None:
            xhat = results.xhat = {}
            for stage in sp.scenario_tree.stages[:-1]:
                for node in stage.nodes:
                    node_xhat = xhat[node.name] = {}
                    reference_scenario = node.scenarios[0]
                    node_x = reference_scenario._x[node.name]
                    for id_ in node._variable_ids:
                        node_xhat[id_] = node_x[id_]
                    # TODO: stage costs
                    #for stagenum, stage in enumerate(sp.scenario_tree.stages[:-1]):
                    #    cost_variable_name, cost_variable_index = \
                    #        stage._cost_variable
                    #    stage_cost_obj = \
                    #        instance.find_component(cost_variable_name)[cost_variable_index]
                    #    if not stage_cost_obj.is_expression_type():
                    #        refvar = ComponentUID(stage_cost_obj,cuid_buffer=tmp).\
                    #            find_component(reference_model)
                    #        refvar.value = stage_cost_obj.value
                    #        refvar.stale = stage_cost_obj.stale

        return results
Esempio n. 2
0
    def _solve_impl(self,
                    sp,
                    output_solver_log=False,
                    verbose=False,
                    logfile=None,
                    **kwds):
        """
        Solve a stochastic program with the SchurIpopt solver.

        See the 'solve' method on the base class for
        additional keyword documentation.

        Args:
            sp: The stochastic program to solve.
            output_solver_log (bool): Stream the solver
                output during the solve.
            logfile: The name of the logfile to save the
                solver output into.
            verbose: Report verbose status information to
                aid debugging.
            **kwds: Passed to the DDSIP file writer as I/O
              options (e.g., symbolic_solver_labels=True).

        Returns: A results object with information about the solution.
        """

        #
        # Setup the SchurIpopt working directory
        #
        problem_list_filename = "PySP_Subproblems.txt"
        working_directory = self._create_tempdir("workdir",
                                                 dir=os.getcwd())

        if logfile is None:
            logfile = os.path.join(working_directory,
                                   "schuripopt.log")
            self._add_tempfile("logfile", logfile)
        else:
            self._files["logfile"] = logfile

        if verbose:
            print("Schuripopt solver working directory: %s"
                  % (working_directory))
            print("Schuripopt solver logfile: %s"
                  % (logfile))

        #
        # Launch SchurIpopt from the worker processes
        # (assumed to be launched together using mpirun)
        #
        status = self._launch_solver(
            sp,
            working_directory,
            logfile=logfile,
            output_solver_log=output_solver_log,
            verbose=verbose,
            io_options=kwds)

        objective = 0.0
        solver_status = set()
        solver_message = set()
        termination_condition = set()
        solution_status = set()
        if status.solve_type == "bundles":
            assert sp.scenario_tree.contains_bundles()
            assert len(status.objective) == \
                len(sp.scenario_tree.bundles)
            for bundle in sp.scenario_tree.bundles:
                if objective is not None:
                    if isinstance(status.objective[bundle.name], UndefinedData):
                        objective = None
                    else:
                        objective += bundle.probability * \
                                     status.objective[bundle.name]
                solver_status.add(status.solver_status[bundle.name])
                solver_message.add(status.solver_message[bundle.name])
                termination_condition.add(status.termination_condition[bundle.name])
                if isinstance(status.solution_status[bundle.name], UndefinedData):
                    solution_status.add(None)
                else:
                    solution_status.add(status.solution_status[bundle.name])
        else:
            assert status.solve_type == "scenarios"
            assert len(status.objective) == \
                len(sp.scenario_tree.scenarios)
            for scenario in sp.scenario_tree.scenarios:
                if objective is not None:
                    if isinstance(status.objective[scenario.name], UndefinedData):
                        objective = None
                    else:
                        objective += scenario.probability * \
                                     status.objective[scenario.name]
                solver_status.add(status.solver_status[scenario.name])
                solver_message.add(status.solver_message[scenario.name])
                termination_condition.add(status.termination_condition[scenario.name])
                if isinstance(status.solution_status[scenario.name], UndefinedData):
                    solution_status.add(None)
                else:
                    solution_status.add(status.solution_status[scenario.name])

        assert len(solver_status) == 1
        assert len(solver_message) == 1
        assert len(termination_condition) == 1
        assert len(solution_status) == 1

        results = SPSolverResults()
        results.objective = None
        results.bound = None
        results.status = solution_status.pop()
        results.solver.status = solver_status.pop()
        results.solver.termination_condition = termination_condition.pop()
        results.solver.message = solver_message.pop()
        results.solver.time = max(status.solve_time.values())
        results.solver.pyomo_time = \
            max(status.pyomo_solve_time.values())

        results.xhat = None
        if str(results.solver.status) == "ok" and \
           str(results.solver.termination_condition) == "optimal":
            results.objective = objective
            xhat = results.xhat = {}
            for stage in sp.scenario_tree.stages[:-1]:
                for node in stage.nodes:
                    worker_name = sp.get_worker_for_scenario(
                        node.scenarios[0].name)
                    node_solution = sp.invoke_function_on_worker(
                        worker_name,
                        "EXTERNAL_collect_solution",
                        thisfile,
                        invocation_type=InvocationType.Single,
                        function_args=(node.name,))
                    node_xhat = xhat[node.name] = {}
                    for id_ in node_solution:
                        node_xhat[repr(id_)] = node_solution[id_][0]

        return results
Esempio n. 3
0
File: ef.py Progetto: CanLi1/pyomo-1
    def _solve_impl(self,
                    sp,
                    output_solver_log=False,
                    keep_solver_files=False,
                    symbolic_solver_labels=False):
        """
        Solve a stochastic program by building the extensive
        form and calling and a Pyomo solver.

        See the 'solve' method on the base class for
        additional keyword documentation.

        Args:
            sp: The stochastic program to solve. Pyro based
                managers are not accepted. All scenario
                models must be managed locally.
            output_solver_log (bool): Stream the solver
                output during the solve.
            keep_solver_files (bool): Retain temporary solver
                input and output files after the solve completes.
            symbolic_solver_labels (bool): Generate solver
                input files using human-readable symbols
                (makes debugging easier).

        Returns: A results object with information about the solution.
        """

        if isinstance(sp, (ScenarioTreeManagerClientPyro,
                           ScenarioTreeManagerSolverClientPyro)):
            raise TypeError("The EF solver does not handle "
                            "Pyro-based scenario tree managers")

        orig_parents = {}
        if sp.scenario_tree.contains_bundles():
            for scenario in sp.scenario_tree.scenarios:
                if scenario._instance._parent is not None:
                    orig_parents[scenario] = scenario._instance._parent
                    scenario._instance._parent = None
                    assert not scenario._instance_objective.active
                    scenario._instance_objective.activate()
        try:
            with ExtensiveFormAlgorithm(sp, self._options) as ef:
                ef.build_ef()
                ef.solve(output_solver_log=output_solver_log,
                         keep_solver_files=keep_solver_files,
                         symbolic_solver_labels=symbolic_solver_labels,
                         check_status=False)
        finally:
            for scenario, parent in orig_parents.items():
                scenario._instance._parent = parent
                assert scenario._instance_objective.active
                scenario._instance_objective.deactivate()

        results = SPSolverResults()
        results.objective = ef.objective
        results.bound = ef.bound
        results.status = ef.solution_status
        results.solver.status = ef.solver_status
        results.solver.termination_condition = ef.termination_condition
        results.solver.message = ef.termination_message
        results.solver.time = ef.time
        results.solver.pyomo_time = ef.pyomo_time
        results.xhat = None
        if ef.solution_status is not None:
            xhat = results.xhat = {}
            for stage in sp.scenario_tree.stages[:-1]:
                for node in stage.nodes:
                    node_xhat = xhat[node.name] = {}
                    reference_scenario = node.scenarios[0]
                    node_x = reference_scenario._x[node.name]
                    for id_ in node._variable_ids:
                        node_xhat[id_] = node_x[id_]
                    # TODO: stage costs
                    #for stagenum, stage in enumerate(sp.scenario_tree.stages[:-1]):
                    #    cost_variable_name, cost_variable_index = \
                    #        stage._cost_variable
                    #    stage_cost_obj = \
                    #        instance.find_component(cost_variable_name)[cost_variable_index]
                    #    if not stage_cost_obj.is_expression_type():
                    #        refvar = ComponentUID(stage_cost_obj,cuid_buffer=tmp).\
                    #            find_component(reference_model)
                    #        refvar.value = stage_cost_obj.value
                    #        refvar.stale = stage_cost_obj.stale

        return results
Esempio n. 4
0
    def _solve_impl(self,
                    sp,
                    rho=1.0,
                    y_init=0.0,
                    z_init=0.0,
                    output_solver_log=False):

        if len(sp.scenario_tree.stages) > 2:
            raise ValueError("ADMM solver does not yet handle more "
                             "than 2 time-stages")

        start_time = time.time()

        scenario_tree = sp.scenario_tree
        num_scenarios = len(scenario_tree.scenarios)
        num_stages = len(scenario_tree.stages)
        num_na_nodes = 0
        num_na_variables = 0
        num_na_continuous_variables = 0
        num_na_binary_variables = 0
        num_na_integer_variables = 0
        for stage in sp.scenario_tree.stages[:-1]:
            for tree_node in stage.nodes:
                num_na_nodes += 1
                num_na_variables += len(tree_node._standard_variable_ids)
                for id_ in tree_node._standard_variable_ids:
                    if tree_node.is_variable_binary(id_):
                        num_na_binary_variables += 1
                    elif tree_node.is_variable_integer(id_):
                        num_na_integer_variables += 1
                    else:
                        num_na_continuous_variables += 1


#        print("-"*20)
#        print("Problem Statistics".center(20))
#        print("-"*20)
#        print("Total number of scenarios.................: %10s"
#              % (num_scenarios))
#        print("Total number of time stages...............: %10s"
#              % (num_stages))
#        print("Total number of non-anticipative nodes....: %10s"
#              % (num_na_nodes))
#        print("Total number of non-anticipative variables: %10s\n#"
#              "                                continuous: %10s\n#"
#              "                                    binary: %10s\n#"
#              "                                   integer: %10s"
#              % (num_na_variables,
#                 num_na_continuous_variables,
#                 num_na_binary_variables,
#                 num_na_integer_variables))

        rel_tol_primal = \
            self.get_option("primal_residual_relative_tolerance")
        rel_tol_dual = \
            self.get_option("dual_residual_relative_tolerance")
        max_iterations = \
            self.get_option("max_iterations")

        self.objective_history = OrderedDict()
        self.primal_residual_history = OrderedDict()
        self.dual_residual_history = OrderedDict()
        self.iterations = 0
        if output_solver_log:
            print("")
        label_cols = ("{0:^4} {1:>16} {2:>8} {3:>8} {4:>12}".format(
            "iter", "objective", "pr_res", "du_res", "lg(||rho||)"))
        with ADMMAlgorithm(sp, self._options) as admm:
            rho, x, y, z = admm.initialize_algorithm_data(rho_init=rho,
                                                          y_init=y_init,
                                                          z_init=z_init)
            rho_strategy = RhoStrategyFactory(self.get_option("rho_strategy"),
                                              self._options)
            rho_strategy.initialize(sp, x, y, z, rho)
            for i in xrange(max_iterations):

                objective = \
                    admm.run_x_update(x, y, z, rho)
                (unscaled_primal_residual,
                 unscaled_dual_residual,
                 x_scale,
                 z_scale) = \
                    admm.run_z_update(x, y, z, rho)
                y_scale = \
                    admm.run_y_update(x, y, z, rho)

                # we've completed another iteration
                self.iterations += 1

                # check for convergence
                primal_rel_scale = max(1.0, x_scale, z_scale)
                dual_rel_scale = max(1.0, y_scale)
                primal_residual = unscaled_primal_residual / \
                                  math.sqrt(num_scenarios) / \
                                  primal_rel_scale
                dual_residual = unscaled_dual_residual / \
                                math.sqrt(num_na_variables) / \
                                dual_rel_scale

                self.objective_history[i] = \
                    objective
                self.primal_residual_history[i] = \
                    primal_residual
                self.dual_residual_history[i] = \
                    dual_residual

                if output_solver_log:
                    if (i % 10) == 0:
                        print(label_cols)
                    print("%4d %16.7e %8.2e %8.2e %12.2e" %
                          (i, objective, primal_residual, dual_residual,
                           math.log(admm.compute_nodevector_norm(rho))))

                if (primal_residual < rel_tol_primal) and \
                   (dual_residual < rel_tol_dual):
                    if output_solver_log:
                        print("\nNumber of Iterations....: %s" %
                              (self.iterations))
                    break
                else:
                    rho_strategy.update_rho(sp, x, y, z, rho)

            else:
                if output_solver_log:
                    print("\nMaximum number of iterations reached: %s" %
                          (max_iterations))

        if output_solver_log:
            print("")
            print("                        {0:^24} {1:^24}".\
                  format("(scaled)", "(unscaled)"))
            print("Objective..........:    {0:^24} {1:^24.16e}".\
                  format("-", objective))
            print("Primal residual....:    {0:^24.16e} {1:^24.16e}".\
                  format(primal_residual, unscaled_primal_residual))
            print("Dual residual......:    {0:^24.16e} {1:^24.16e}".\
                  format(dual_residual, unscaled_dual_residual))
            unscaled_err = unscaled_primal_residual + \
                           unscaled_dual_residual
            err = primal_residual + dual_residual
            print("Overall error......:    {0:^24.16e} {1:^24.16e}".\
                  format(err, unscaled_err))

        results = SPSolverResults()
        results.objective = objective
        results.xhat = z
        return results
Esempio n. 5
0
File: admm.py Progetto: Pyomo/pyomo
    def _solve_impl(self,
                    sp,
                    rho=1.0,
                    y_init=0.0,
                    z_init=0.0,
                    output_solver_log=False):

        if len(sp.scenario_tree.stages) > 2:
            raise ValueError(
                "ADMM solver does not yet handle more "
                "than 2 time-stages")

        start_time = time.time()

        scenario_tree = sp.scenario_tree
        num_scenarios = len(scenario_tree.scenarios)
        num_stages = len(scenario_tree.stages)
        num_na_nodes = 0
        num_na_variables = 0
        num_na_continuous_variables = 0
        num_na_binary_variables = 0
        num_na_integer_variables = 0
        for stage in sp.scenario_tree.stages[:-1]:
            for tree_node in stage.nodes:
                num_na_nodes += 1
                num_na_variables += len(tree_node._standard_variable_ids)
                for id_ in tree_node._standard_variable_ids:
                    if tree_node.is_variable_binary(id_):
                        num_na_binary_variables += 1
                    elif tree_node.is_variable_integer(id_):
                        num_na_integer_variables += 1
                    else:
                        num_na_continuous_variables += 1

#        print("-"*20)
#        print("Problem Statistics".center(20))
#        print("-"*20)
#        print("Total number of scenarios.................: %10s"
#              % (num_scenarios))
#        print("Total number of time stages...............: %10s"
#              % (num_stages))
#        print("Total number of non-anticipative nodes....: %10s"
#              % (num_na_nodes))
#        print("Total number of non-anticipative variables: %10s\n#"
#              "                                continuous: %10s\n#"
#              "                                    binary: %10s\n#"
#              "                                   integer: %10s"
#              % (num_na_variables,
#                 num_na_continuous_variables,
#                 num_na_binary_variables,
#                 num_na_integer_variables))

        rel_tol_primal = \
            self.get_option("primal_residual_relative_tolerance")
        rel_tol_dual = \
            self.get_option("dual_residual_relative_tolerance")
        max_iterations = \
            self.get_option("max_iterations")

        self.objective_history = OrderedDict()
        self.primal_residual_history = OrderedDict()
        self.dual_residual_history = OrderedDict()
        self.iterations = 0
        if output_solver_log:
            print("")
        label_cols = ("{0:^4} {1:>16} {2:>8} {3:>8} {4:>12}".format(
            "iter","objective","pr_res","du_res","lg(||rho||)"))
        with ADMMAlgorithm(sp, self._options) as admm:
            rho, x, y, z = admm.initialize_algorithm_data(rho_init=rho,
                                                          y_init=y_init,
                                                          z_init=z_init)
            rho_strategy = RhoStrategyFactory(
                self.get_option("rho_strategy"),
                self._options)
            rho_strategy.initialize(sp, x, y, z, rho)
            for i in xrange(max_iterations):

                objective = \
                    admm.run_x_update(x, y, z, rho)
                (unscaled_primal_residual,
                 unscaled_dual_residual,
                 x_scale,
                 z_scale) = \
                    admm.run_z_update(x, y, z, rho)
                y_scale = \
                    admm.run_y_update(x, y, z, rho)

                # we've completed another iteration
                self.iterations += 1

                # check for convergence
                primal_rel_scale = max(1.0, x_scale, z_scale)
                dual_rel_scale = max(1.0, y_scale)
                primal_residual = unscaled_primal_residual / \
                                  math.sqrt(num_scenarios) / \
                                  primal_rel_scale
                dual_residual = unscaled_dual_residual / \
                                math.sqrt(num_na_variables) / \
                                dual_rel_scale

                self.objective_history[i] = \
                    objective
                self.primal_residual_history[i] = \
                    primal_residual
                self.dual_residual_history[i] = \
                    dual_residual

                if output_solver_log:
                    if (i % 10) == 0:
                        print(label_cols)
                    print("%4d %16.7e %8.2e %8.2e %12.2e"
                          % (i,
                             objective,
                             primal_residual,
                             dual_residual,
                             math.log(admm.compute_nodevector_norm(rho))))

                if (primal_residual < rel_tol_primal) and \
                   (dual_residual < rel_tol_dual):
                    if output_solver_log:
                        print("\nNumber of Iterations....: %s"
                              % (self.iterations))
                    break
                else:
                    rho_strategy.update_rho(sp, x, y, z, rho)

            else:
                if output_solver_log:
                    print("\nMaximum number of iterations reached: %s"
                          % (max_iterations))

        if output_solver_log:
            print("")
            print("                        {0:^24} {1:^24}".\
                  format("(scaled)", "(unscaled)"))
            print("Objective..........:    {0:^24} {1:^24.16e}".\
                  format("-", objective))
            print("Primal residual....:    {0:^24.16e} {1:^24.16e}".\
                  format(primal_residual, unscaled_primal_residual))
            print("Dual residual......:    {0:^24.16e} {1:^24.16e}".\
                  format(dual_residual, unscaled_dual_residual))
            unscaled_err = unscaled_primal_residual + \
                           unscaled_dual_residual
            err = primal_residual + dual_residual
            print("Overall error......:    {0:^24.16e} {1:^24.16e}".\
                  format(err, unscaled_err))

        results = SPSolverResults()
        results.objective = objective
        results.xhat = z
        return results
Esempio n. 6
0
    def _solve_impl(self,
                    sp,
                    output_solver_log=False,
                    verbose=False,
                    logfile=None,
                    **kwds):
        """
        Solve a stochastic program with the SchurIpopt solver.

        See the 'solve' method on the base class for
        additional keyword documentation.

        Args:
            sp: The stochastic program to solve.
            output_solver_log (bool): Stream the solver
                output during the solve.
            logfile: The name of the logfile to save the
                solver output into.
            verbose: Report verbose status information to
                aid debugging.
            **kwds: Passed to the DDSIP file writer as I/O
              options (e.g., symbolic_solver_labels=True).

        Returns: A results object with information about the solution.
        """

        #
        # Setup the SchurIpopt working directory
        #
        problem_list_filename = "PySP_Subproblems.txt"
        working_directory = self._create_tempdir("workdir",
                                                 dir=os.getcwd())

        if logfile is None:
            logfile = os.path.join(working_directory,
                                   "schuripopt.log")
            self._add_tempfile("logfile", logfile)
        else:
            self._files["logfile"] = logfile

        if verbose:
            print("Schuripopt solver working directory: %s"
                  % (working_directory))
            print("Schuripopt solver logfile: %s"
                  % (logfile))

        #
        # Launch SchurIpopt from the worker processes
        # (assumed to be launched together using mpirun)
        #
        status = self._launch_solver(
            sp,
            working_directory,
            logfile=logfile,
            output_solver_log=output_solver_log,
            verbose=verbose,
            io_options=kwds)

        objective = 0.0
        solver_status = set()
        solver_message = set()
        termination_condition = set()
        solution_status = set()
        if status.solve_type == "bundles":
            assert sp.scenario_tree.contains_bundles()
            assert len(status.objective) == \
                len(sp.scenario_tree.bundles)
            for bundle in sp.scenario_tree.bundles:
                if objective is not None:
                    if isinstance(status.objective[bundle.name], UndefinedData):
                        objective = None
                    else:
                        objective += bundle.probability * \
                                     status.objective[bundle.name]
                solver_status.add(status.solver_status[bundle.name])
                solver_message.add(status.solver_message[bundle.name])
                termination_condition.add(status.termination_condition[bundle.name])
                if isinstance(status.solution_status[bundle.name], UndefinedData):
                    solution_status.add(None)
                else:
                    solution_status.add(status.solution_status[bundle.name])
        else:
            assert status.solve_type == "scenarios"
            assert len(status.objective) == \
                len(sp.scenario_tree.scenarios)
            for scenario in sp.scenario_tree.scenarios:
                if objective is not None:
                    if isinstance(status.objective[scenario.name], UndefinedData):
                        objective = None
                    else:
                        objective += scenario.probability * \
                                     status.objective[scenario.name]
                solver_status.add(status.solver_status[scenario.name])
                solver_message.add(status.solver_message[scenario.name])
                termination_condition.add(status.termination_condition[scenario.name])
                if isinstance(status.solution_status[scenario.name], UndefinedData):
                    solution_status.add(None)
                else:
                    solution_status.add(status.solution_status[scenario.name])

        assert len(solver_status) == 1
        assert len(solver_message) == 1
        assert len(termination_condition) == 1
        assert len(solution_status) == 1

        results = SPSolverResults()
        results.objective = None
        results.bound = None
        results.status = solution_status.pop()
        results.solver.status = solver_status.pop()
        results.solver.termination_condition = termination_condition.pop()
        results.solver.message = solver_message.pop()
        results.solver.time = max(status.solve_time.values())
        results.solver.pyomo_time = \
            max(status.pyomo_solve_time.values())

        results.xhat = None
        if str(results.solver.status) == "ok" and \
           str(results.solver.termination_condition) == "optimal":
            results.objective = objective
            xhat = results.xhat = {}
            for stage in sp.scenario_tree.stages[:-1]:
                for node in stage.nodes:
                    worker_name = sp.get_worker_for_scenario(
                        node.scenarios[0].name)
                    node_solution = sp.invoke_function_on_worker(
                        worker_name,
                        "EXTERNAL_collect_solution",
                        thisfile,
                        invocation_type=InvocationType.Single,
                        function_args=(node.name,))
                    node_xhat = xhat[node.name] = {}
                    for id_ in node_solution:
                        node_xhat[repr(id_)] = node_solution[id_][0]

        return results