def test_equal(): result1 = Result(solver_type=SolverType.FFD, time_us=100, lengths=[(100, 100, 100), (200, 200, 200)]) result2 = Result(solver_type=SolverType.FFD, time_us=200, lengths=[(100, 100, 100), (200, 200, 200)]) assert result1 == result2
def test_exactly(): result1 = Result(solver_type=SolverType.FFD, time_us=100, lengths=[(100, 100, 100), (200, 200, 200)]) result2 = Result(solver_type=SolverType.FFD, time_us=200, lengths=[(100, 100, 100), (200, 200, 200)]) result3 = Result(solver_type=SolverType.FFD, time_us=200, lengths=[(100, 100, 100), (200, 200, 200)]) assert not result1.exactly(result2) assert result2.exactly(result3)
def _solve_gapfill(job: Job) -> Result: # 1. Sort by magnitude (largest first) # 2. stack until limit is reached # 3. try smaller as long as possible # 4. create new bar # we are writing around in target sizes, prevent leaking changes to job mutable_sizes = copy.deepcopy(job.target_sizes) targets = sorted(mutable_sizes, reverse=True) stocks = [] current_size = 0 current_stock = [] i_target = 0 while len(targets) > 0: # nothing fit, next stock if i_target >= len(targets): # add local result stocks.append(current_stock) # reset current_stock = [] current_size = 0 i_target = 0 current_target = targets[i_target] # target fits inside current stock, transfer to results if (current_size + current_target.length + job.cut_width) < job.max_length: current_stock.append(current_target.length) current_size += current_target.length + job.cut_width # remove empty entries if current_target.quantity <= 1: targets.remove(current_target) else: current_target.quantity -= 1 # try smaller else: i_target += 1 # apply last "forgotten" stock if current_stock: stocks.append(current_stock) # trimming could be calculated from len(stocks) * length - sum(stocks) return Result(solver_type=SolverType.gapfill, lengths=stocks)
def test_full_model(): json_job = Path("./tests/data/in/testjob.json") assert json_job.exists() json_result = Path("./tests/data/out/testresult.json") with open(json_job, "r") as encoded_job: job = Job.parse_raw(encoded_job.read()) solved = distribute(job) encoded_solved = solved.json() assert len(encoded_solved) > 20 with open(json_result, "r") as encoded_result: result = Result.parse_raw(encoded_result.read()) assert solved == result
def _solve_FFD(job: Job) -> Result: # iterate over list of stocks # put into first stock that it fits into # 1. Sort by magnitude (largest first) # 2. stack until limit is reached # 3. try smaller as long as possible # 4. create new bar mutable_sizes = copy.deepcopy(job.target_sizes) targets = sorted(mutable_sizes, reverse=True) assert len(targets) > 0 stocks: List[List[int]] = [[]] stock_lengths: List[int] = [0] i_target = 0 while i_target < len(targets): current_size = targets[i_target] for i, stock in enumerate(stocks): # step through existing stocks until current size fits if (job.max_length - stock_lengths[i]) > current_size.length: # add size stock.append(current_size.length) stock_lengths[i] += job.cut_width + current_size.length break else: # nothing fit, opening next bin stocks.append([current_size.length]) stock_lengths.append(0) assert len(stocks) == len(stock_lengths) # decrease/get next if current_size.quantity <= 1: i_target += 1 else: current_size.quantity -= 1 return Result(solver_type=SolverType.FFD, lengths=stocks)
def _solve_bruteforce(job: Job) -> Result: # failsafe if len(job) > 12: raise OverflowError("Input too large") # find every possible ordering (n! elements) all_orderings = permutations(job.get_sizes()) # TODO: remove duplicates (due to "quantity") # "infinity" min_trimmings = len(job) * job.max_length min_stocks: List[List[int]] = [] # possible improvement: Distribute combinations to multiprocessing worker threads for combination in all_orderings: stocks, trimmings = _split_combination(combination, job.max_length, job.cut_width) if trimmings < min_trimmings: min_stocks = stocks min_trimmings = trimmings return Result(solver_type=SolverType.bruteforce, lengths=min_stocks)
def test_constructor(): result = Result(solver_type=SolverType.FFD, time_us=100, lengths=[(100, 100, 100), (200, 200, 200)]) assert result
def test_invalid(): invalid_result = Result(solver_type=SolverType.FFD, time_us=-1, lengths=[(100, 100, 100), (200, 200, 200)]) assert not invalid_result.valid()
def test_valid(): result = Result(solver_type=SolverType.FFD, time_us=100, lengths=[(100, 100, 100), (200, 200, 200)]) assert result.valid()