Commit 12515c87 authored by Kento HASEGAWA's avatar Kento HASEGAWA
Browse files

Support for split-and-merge strategy

parent 392c18f3
Loading
Loading
Loading
Loading
+5 −1
Changes for adc2019system.py: 5 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@

import json

from roles import Host, Solver
from roles import Host, Solver, MergeSolver

config = None
role = None
@@ -32,7 +32,11 @@ def set_role(role_name, config_data):
    if role_name == 'host':
        role = Host(config_data)
    elif role_name == 'solver':
        if not 'partial_mode' in config_data:
            config['partial_mode'] = False
        role = Solver(config_data)
    elif role_name == 'merge_solver':
        role = MergeSolver(config_data)

def call_api(method, cmd, params):

+1 −0
Changes for roles/__init__.py: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
from .host import Host
from .solver import Solver
from .merge_solver import MergeSolver
+27 −4
Changes for roles/host.py: 27 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@ import threading
import time
import requests

from utils import Problem
from utils import Problem, GroupPart
import utils.adcclilib as adccli

class Host(object):
@@ -74,7 +74,31 @@ class Host(object):
            return None
    
    def store_solution(self, solution):
        # request_idをチェックする機能もつくておく
        
        if 'part_id' in solution:
            if solution['status'] != 'done':
                return {'status': 'ignored'}
            problem_key = solution['problem']

            if problem_key in self.problems:
                res = self.problems[problem_key].put_partial_solution(solution)
            else:
                res = False
            
            print(solution['solution'])
            print(solution['line_map'], solution['block_map'])

            if res:
                merge_problem = self.problems[problem_key].partial_merge_problem
                # print(merge_problem)
                merge_problem['request_id'] = solution['request_id']
                merge_problem['timeout'] = 10000
                for k, v in self.worker_manager.workers.items():
                    if v.role == 'merge_solver':
                        v.post('solve', merge_problem)
                return {'status': 'done'}

        else:
            request_id = solution['request_id']
            if request_id in self.request:
                self.request[request_id].store_response(solution)
@@ -259,7 +283,7 @@ class Worker(object):
        self.host = params['host']
        self.role = params['role']
        self.params = params
        self.status = 'Not ready'
        self.status = 'Ready'

        self.configure()
    
@@ -316,7 +340,6 @@ class Request(object):
        return self.request_data
    
    def store_response(self, data):
        # TODO: 1つのrequest_idに対し同一のworkerから2つ以上答えが返ってきた場合の例外処理
        worker = data['worker']
        self.response[worker] = data

roles/merge_solver.py

0 → 100644
+79 −0
Changes for roles/merge_solver.py: 79 added lines, 0 removed lines.
Original line number Diff line number Diff line
import re

from roles import Solver

class MergeSolver(Solver):

    def __init__(self, config):
        super().__init__(config)

        self.type = 'merge_solver'
        self.partial_mode = False

    def __repr__(self):
        return "MergeSolver"

    def start_solver(self, params):

        if 'merge_problem' in params:
            return super().start_solver(params)
        else:
            return {'status': 'N/A'}

    def submit_solution(self, params, solution):

        if solution['status'] == 'done':
            _lines = solution['solution'].splitlines()

            parts_pos = list()

            for _l in _lines:
                l = _l.strip()
                if l.startswith('SIZE'):
                    board_size_str = l.strip().split()[1]
                    board_solution_size = [int(v) for v in board_size_str.split('X')]
                    bw = board_solution_size[0]
                    bh = board_solution_size[1]
                elif l.startswith('BLOCK'):
                    print(l)
                    p = r'BLOCK#([0-9]+) +@\(([0-9]+), *([0-9]+)\)'
                    m = re.match(p, l)
                    bi = int(m.group(1))
                    bx = int(m.group(2))
                    by = int(m.group(3))
                    parts_pos.append((bx, by))
            
            _map = list()
            _blocks = dict()
            for _y in range(bh):
                _map.append([0 for _x in range(bw)])

            for (px, py), part in zip(parts_pos, params['parts']):
                for _y, _r in enumerate(part['remap']):
                    for _x, _c in enumerate(_r):
                        if _c != 0:
                            _map[py+_y][px+_x] = _c
            
            for (px, py), part in zip(parts_pos, params['parts']):
                for bi, (_bx, _by) in part['remap_block'].items():
                    _blocks[bi] = (px+_bx, py+_by)
            
            _sorted_blocks = sorted(_blocks.items(), key=lambda x:x[0])

            solution_text = ''
            solution_text += f'SIZE {bw}X{bh}\n'
            for _r in _map:
                solution_text += ','.join([str(v) for v in _r])
                solution_text += '\n'
            for bi, (_bx, _by) in _sorted_blocks:
                solution_text += f'BLOCK#{bi} @({_bx},{_by})\n'

            solution['solution'] = solution_text

        data = {
            'request_id': params['request_id'],
            'problem': params['name'],
            'worker': self.address
        }
        data.update(solution)
        self.post('problem/solution', data)
+30 −4
Changes for roles/solver.py: 30 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -5,8 +5,10 @@ import requests
import sys
import time
import threading
import traceback

from collections import OrderedDict
from utils import Problem

class Solver(object):

@@ -16,6 +18,7 @@ class Solver(object):
        self.host = config['host']
        self.address = config['address']
        self.name = config['name']
        self.partial_mode = config['partial_mode']
        self.solver = importlib.import_module(f"solvers.{config['solver']}")
        self.queue = OrderedDict()

@@ -50,6 +53,7 @@ class Solver(object):
                    self.solve(params)
                except Exception as e:
                    print("E: An error has occurred in the solver thread")
                    print(traceback.format_exc())
                self.solving = None
                self.set_status('Ready')
            else:
@@ -57,6 +61,28 @@ class Solver(object):
    
    def solve(self, params):

        if self.partial_mode and len(params['group_problems']) > 1:

            for i, (gproblem, gline_map, gblock_map) in enumerate(params['group_problems']):

                data = params.copy()
                data['problem'] = gproblem
                
                start_time = time.time()
                solution = self.solver.solve(data)
                end_time = time.time()

                elapsed_time = end_time - start_time

                if not 'elapsed_time' in solution:
                    solution['elapsed_time'] = elapsed_time
                
                solution['part_id'] = i
                solution['line_map'] = gline_map
                solution['block_map'] = gblock_map

                self.submit_solution(data, solution)
        else:
            start_time = time.time()
            solution = self.solver.solve(params)
            end_time = time.time()
@@ -114,15 +140,15 @@ class Solver(object):
            for k, v in self.queue.items():
                if k == request_id:
                    self.queue.pop(k, None)
            if self.solving['request_id'] == request_id:
                self.solver.stop()()
            if self.solving is not None and self.solving['request_id'] == request_id:
                self.solver.stop()
        elif 'problem' in params:
            problem_name = params['problem']
            for k, v in self.queue.items():
                if v['name'] == problem_name:
                    self.queue.pop(k, None)
            if self.solving['name'] == problem_name:
                self.solver.stop()()
            if self.solving is not None and self.solving['name'] == problem_name:
                self.solver.stop()

        return {'status': 'canceled'}

Loading