Rust中的多线程分支和绑定搜索

时间:2018-11-25 15:05:17

标签: multithreading rust dynamic-programming lifetime

我正在尝试在Rust中使用分支定界算法解决数学最小化问题。为简单起见,假设我们只是在试图找到目标值。

功能

要解决此问题,需要解决一个更简单的版本(“松弛”),然后可能会产生两个子问题。解决松弛需要很长时间,并且需要在单独的线程上进行。

困难在于问题的子问题只有在解决问题后才知道。这样,问题及其子问题所在的二叉树在计算过程中会不断增长。此外,松弛的解决可能导致树的节点被修剪。对于每个已知问题,必须存储一个相当大的表。由于内存容量有限,我想按深度优先的顺序搜索此树。

不起作用

树的性能无关紧要;绝大多数时间将花费在解决松弛问题上。我想避免使用手动相对引用和类似的构造,而是使用Rust的引用工具箱来解决此问题。另外,我想捕获类型系统中问题节点的生命周期:

  1. 已知问题
  2. 正在解决
  3. 松弛已得到解决,并且
    • 如果问题切实可行:
      • 如果松弛的目标值低于全局最大值,则计算子问题
      • 如果不是,则将问题标记为次优,其他子问题则无关紧要
    li>
  4. 否则,该问题被标记为不可行,其他子问题不相关
  5. 两个子问题都已解决,仅存储了该问题的客观价值
  6. >

尝试的例子

我尝试了几种方法,但是我一直遇到问题。我的最新方法最好通过树中节点的定义来概括。问题数据存储在Tableau中。

enum Node<'a, T, TP> {
    /// The problem to solve. Nothing is known.
    Undecided {
        parent: Option<rc::Weak<Self>>,
        depth: u64,
        tableau: Tableau<'a, T, TP>,
    },
    /// Being calculated
    ActiveCalculation {
        parent: Option<rc::Weak<Self>>,
        depth: u64,
        tableau: Arc<Mutex<Tableau<'a, T, TP>>>,
        sender_to_active_thread: Sender<PruneReason>,
    },
    /// The problem is solved, and the children (if any) should be created while this variant is
    /// being instantiated.
    NodeOptimal {
        parent: Option<Weak<Self>>,
        relaxation_value: f64,
        best_lower_bound: Cell<Option<f64>>,
        lower: Rc<Self>,
        upper: Rc<Self>,
    },
    /// This problem and all generated subproblems are solved.
    SubTreeOptimal {
        lower_bound: f64,
    },
    /// Pruned.
    Pruned(PruneReason), // e.g. SubOptimal, Infeasible
}

我尝试用主线程管理树,同时为工作线程提供问题数据的Arc。当新发现的信息确定该计算只能产生次优结果时,sender_to_active_thread变量上的ActiveCalculation字段用于终止计算。

上述尝试的问题在于,一旦找到解决方案,我将不知道如何更新树。参见下面的代码,该代码从树中获取下一个问题,将其交给线程处理,并处理结果:

let (solution_sender, solution_receiver) = channel();

    // Stop condition
    while !tree.finished() {

        let mut possible_next_problem = tree.next_problem();
        // Wait condition
        while active_threads == max_threads || possible_next_problem.is_some() {
            // Wait for a signal, block until a thread has terminated
            let (solved_problem, result) = solution_receiver.recv().unwrap();
            active_threads -= 1;

            let new_node = match result {
                None => Node::Pruned(PruneReason::Infeasible),
                Some((solution, objective_value)) => {
                    unimplemented!()
                }
            };
            tree.update(solved_problem, new_node);
            possible_next_problem = tree.next_problem();
        }
        // Assumed to be of `Undecided` variant
        let next_problem = possible_next_problem.unwrap();

        let solution_sender_clone = solution_sender.clone();
        let (termination_sender, termination_receiver) = channel();
        *next_problem = next_problem.undecided_to_active_calculation(termination_sender);
        let pointer_to_problem_in_tree = next_problem.clone();
        if let Node::ActiveCalculation { tableau, .. } = *next_problem {
            thread::spawn(move || {
                let result = solve_in_separate_thread(&mut *tableau.lock().expect("Should be of variant `Undecided`"),
                                                      termination_receiver);
                solution_sender_clone.send((pointer_to_problem_in_tree, result)).unwrap();
            });
        } else { panic!("Should be of variant `ActiveCalculation`.") };
    }

编译器告诉我,仅将Arc<Node>移动到线程(然后再次将其发送到主线程)需要Node及其所有字段均为Sync

可以找到代码here

0 个答案:

没有答案