hyf

Context-aware query service for Radroots
git clone https://radroots.dev/git/hyf.git
Log | Files | Refs | README | LICENSE

match_plan.mojo (4812B)


      1 from std.collections import List
      2 
      3 from hyf_core.domain.plan import (
      4     Allocation,
      5     LotCapacity,
      6     MatchPlan,
      7     allocation,
      8     match_plan,
      9     plan_conservation_violations,
     10 )
     11 
     12 
     13 def group_lots_by_supplier(
     14     lot_ids: List[String], supplier_ids: List[String]
     15 ) raises -> List[String]:
     16     if len(lot_ids) != len(supplier_ids):
     17         raise Error("lot ids and supplier ids must align")
     18     var suppliers = List[String]()
     19     for supplier in supplier_ids:
     20         if String(supplier).strip().byte_length() == 0:
     21             raise Error("supplier id must not be empty")
     22         var seen = False
     23         for existing in suppliers:
     24             if existing == supplier:
     25                 seen = True
     26         if not seen:
     27             suppliers.append(String(supplier))
     28     return suppliers^
     29 
     30 
     31 def allocate_single_line(
     32     plan_id: String,
     33     line_id: String,
     34     supplier_id: String,
     35     required_value: Int,
     36     required_scale: Int,
     37     lots: List[LotCapacity],
     38 ) raises -> MatchPlan:
     39     var remaining = required_value
     40     var allocations = List[Allocation]()
     41     for lot in lots:
     42         if remaining <= 0:
     43             break
     44         var capacity = lot.value
     45         var take = remaining if remaining < capacity else capacity
     46         allocations.append(
     47             allocation(lot.lot_id, lot.revision, line_id, take, lot.scale)
     48         )
     49         remaining -= take
     50     return match_plan(plan_id, supplier_id, allocations)
     51 
     52 
     53 def multi_supplier_is_supported() -> Bool:
     54     return False
     55 
     56 
     57 @fieldwise_init
     58 struct PlannerBounds(Copyable, Movable):
     59     var max_candidates: Int
     60     var max_plans: Int
     61 
     62 
     63 def planner_bounds(max_candidates: Int, max_plans: Int) raises -> PlannerBounds:
     64     if max_candidates <= 0 or max_plans <= 0:
     65         raise Error("planner bounds must be positive")
     66     return PlannerBounds(max_candidates=max_candidates, max_plans=max_plans)
     67 
     68 
     69 def enforce_plan_bound(plan_count: Int, bounds: PlannerBounds) raises:
     70     if plan_count > bounds.max_plans:
     71         raise Error("plan enumeration exceeds the bound")
     72 
     73 
     74 def allocate_multiple_lines(
     75     plan_id: String,
     76     supplier_id: String,
     77     line_ids: List[String],
     78     required_values: List[Int],
     79     lots: List[LotCapacity],
     80 ) raises -> MatchPlan:
     81     if len(line_ids) != len(required_values):
     82         raise Error("line ids and requirements must align")
     83     var remaining = List[Int]()
     84     for lot in lots:
     85         remaining.append(lot.value)
     86     var allocations = List[Allocation]()
     87     for index in range(len(line_ids)):
     88         var needed = required_values[index]
     89         for lot_index in range(len(lots)):
     90             if needed <= 0:
     91                 break
     92             if remaining[lot_index] <= 0:
     93                 continue
     94             var capacity = remaining[lot_index]
     95             var take = needed if needed < capacity else capacity
     96             allocations.append(
     97                 allocation(
     98                     lots[lot_index].lot_id,
     99                     lots[lot_index].revision,
    100                     line_ids[index],
    101                     take,
    102                     lots[lot_index].scale,
    103                 )
    104             )
    105             remaining[lot_index] -= take
    106             needed -= take
    107     return match_plan(plan_id, supplier_id, allocations)
    108 
    109 
    110 def shared_lot_allows_concurrent_over_allocation() -> Bool:
    111     return False
    112 
    113 
    114 @fieldwise_init
    115 struct PartialOutcome(Copyable, Movable):
    116     var fulfilled: Bool
    117     var uncovered_value: Int
    118     var permitted: Bool
    119 
    120 
    121 def partial_outcome(
    122     required_value: Int, allocated_value: Int, partial_allowed: Bool
    123 ) raises -> PartialOutcome:
    124     if required_value < 0 or allocated_value < 0:
    125         raise Error("partial outcome requires non-negative quantities")
    126     if allocated_value >= required_value:
    127         return PartialOutcome(fulfilled=True, uncovered_value=0, permitted=True)
    128     var uncovered = required_value - allocated_value
    129     return PartialOutcome(
    130         fulfilled=False, uncovered_value=uncovered, permitted=partial_allowed
    131     )
    132 
    133 
    134 def partial_discloses_deficit() -> Bool:
    135     return True
    136 
    137 
    138 @fieldwise_init
    139 struct PlannerOffer(Copyable, Movable):
    140     var plans: List[MatchPlan]
    141     var supported_mode: String
    142     var unsupported: List[String]
    143     var alternatives_are_simultaneous: Bool
    144 
    145 
    146 def alternative_plans(
    147     plans: List[MatchPlan], unsupported: List[String]
    148 ) raises -> PlannerOffer:
    149     var copied = List[MatchPlan]()
    150     for plan in plans:
    151         copied.append(plan.copy())
    152     var unsupported_copied = List[String]()
    153     for entry in unsupported:
    154         unsupported_copied.append(String(entry))
    155     return PlannerOffer(
    156         plans=copied^,
    157         supported_mode="single_supplier_compatible_lots",
    158         unsupported=unsupported_copied^,
    159         alternatives_are_simultaneous=False,
    160     )
    161 
    162 
    163 def unsupported_mode_means_no_supply() -> Bool:
    164     return False