File size: 1,350 Bytes
cc298f9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
"""Optimization problem: existing stations + candidates + objective + how many to add."""

from dataclasses import dataclass
from functools import cached_property

import numpy as np

from .objective import SeparableObjective
from .placement import PlacementSet


@dataclass
class Problem:
    existing: PlacementSet     # fixed ψ⁰
    candidates: PlacementSet   # 𝒞 — choose m of these
    objective: SeparableObjective
    m: int

    def __post_init__(self):
        if self.existing.travel_times.shape[1] != self.candidates.travel_times.shape[1]:
            raise ValueError("existing and candidates must share the same grid (N)")
        if self.objective.weights.shape[0] != self.candidates.travel_times.shape[1]:
            raise ValueError("objective.weights length must match grid size N")
        if self.m < 0 or self.m > self.candidates.K:
            raise ValueError(f"m must be in [0, K]; got m={self.m}, K={self.candidates.K}")

    @cached_property
    def base_field(self) -> np.ndarray:
        """t⁰ — minimum arrival time over all existing stations."""
        if self.existing.K == 0:
            return np.full(self.candidates.N, np.inf, dtype=np.float64)
        return self.existing.travel_times.min(axis=0)

    @property
    def base_value(self) -> float:
        return self.objective.value(self.base_field)