import numpy as np
import random
from uniform_instance_gen import uni_instance_gen
from CPSolver import CPSolver
from schedule import Schedule

def calculate_machine_utilization(schedule, processing_times, machine_sequences, job_range):
    """
    计算指定工件范围的机器利用率
    
    Args:
        schedule: 调度结果
        processing_times: 加工时间矩阵
        machine_sequences: 机器序列矩阵
        job_range: 工件范围，如 [1, 3] 表示工件1到工件3
    
    Returns:
        float: 机器利用率
    """
    if schedule is None:
        return 0.0
    
    total_processing_time = 0
    total_makespan = schedule.cal_makespan()
    
    # 计算指定范围内工件的总加工时间
    for job_id in job_range:
        if job_id < len(processing_times):
            for machine_id in range(len(processing_times[job_id])):
                if processing_times[job_id][machine_id] > 0:  # 只计算有效的加工时间
                    total_processing_time += processing_times[job_id][machine_id]
    
    if total_makespan == 0:
        return 0.0
    
    # 机器利用率 = 总加工时间 / (机器数量 * 总完工时间)
    num_machines = len(machine_sequences[0]) if len(machine_sequences) > 0 else 1
    utilization = total_processing_time / (num_machines * total_makespan)
    
    return utilization

def solve_subset(instance_data, job_subset):
    """
    求解指定工件子集的调度问题
    
    Args:
        instance_data: 原始实例数据 (processing_times, machine_sequences)
        job_subset: 工件子集，如 [0, 1, 2] 表示工件0,1,2
    
    Returns:
        schedule: 调度结果
    """
    processing_times, machine_sequences = instance_data
    
    # 创建只包含指定工件的子实例
    subset_processing_times = []
    subset_machine_sequences = []
    
    for job_id in job_subset:
        if job_id < len(processing_times):
            subset_processing_times.append(processing_times[job_id].copy())
            subset_machine_sequences.append(machine_sequences[job_id].copy())
    
    subset_data = (subset_processing_times, subset_machine_sequences)
    
    # 使用CP求解器求解
    cp_solver = CPSolver()
    schedule = cp_solver.solve_blocking_job_shop(subset_data, time_limit=60)
    
    return schedule

def analyze_submodular_property(instance_data, x, y):
    """
    分析次模性质
    
    Args:
        instance_data: 实例数据
        x: 第一个随机数
        y: 第二个随机数 (y >= x)
    
    Returns:
        dict: 分析结果
    """
    processing_times, machine_sequences = instance_data
    
    # 定义工件集合，确保不超出实例范围
    num_jobs = len(processing_times)
    S1 = list(range(1, min(x+1, num_jobs)))  # [1, x] 但不超过实际工件数
    S2 = list(range(1, min(y+1, num_jobs)))  # [1, y] 但不超过实际工件数
    S1_with_0 = [0] + S1  # [0, x]
    S2_with_0 = [0] + S2  # [0, y]
    
    print(f"分析工件集合:")
    print(f"S1 = {S1} (工件 {S1[0]} 到 {S1[-1]})")
    print(f"S2 = {S2} (工件 {S2[0]} 到 {S2[-1]})")
    print(f"S1+0 = {S1_with_0} (工件 0 到 {S1[-1]})")
    print(f"S2+0 = {S2_with_0} (工件 0 到 {S2[-1]})")
    
    # 求解各个工件集合的调度
    print("\n求解各个工件集合的调度...")
    
    schedule_S1 = solve_subset(instance_data, S1)
    schedule_S2 = solve_subset(instance_data, S2)
    schedule_S1_0 = solve_subset(instance_data, S1_with_0)
    schedule_S2_0 = solve_subset(instance_data, S2_with_0)
    
    # 计算机器利用率
    util_S1 = calculate_machine_utilization(schedule_S1, processing_times, machine_sequences, S1)
    util_S2 = calculate_machine_utilization(schedule_S2, processing_times, machine_sequences, S2)
    util_S1_0 = calculate_machine_utilization(schedule_S1_0, processing_times, machine_sequences, S1_with_0)
    util_S2_0 = calculate_machine_utilization(schedule_S2_0, processing_times, machine_sequences, S2_with_0)
    
    print(f"\n机器利用率结果:")
    print(f"S1 利用率: {util_S1:.4f}")
    print(f"S2 利用率: {util_S2:.4f}")
    print(f"S1+0 利用率: {util_S1_0:.4f}")
    print(f"S2+0 利用率: {util_S2_0:.4f}")
    
    # 计算边际贡献
    marginal_S1 = util_S1_0 - util_S1  # 工件0对S1的边际贡献
    marginal_S2 = util_S2_0 - util_S2  # 工件0对S2的边际贡献
    
    print(f"\n边际贡献:")
    print(f"工件0对S1的边际贡献: {marginal_S1:.4f}")
    print(f"工件0对S2的边际贡献: {marginal_S2:.4f}")
    
    # 次模性质检查: f(S ∪ {x}) - f(S) 应该随S增大而减小
    # 即 marginal_S1 >= marginal_S2 (因为S1 ⊂ S2)
    is_submodular = marginal_S1 >= marginal_S2
    
    print(f"\n次模性质验证:")
    print(f"边际贡献差值: {marginal_S1 - marginal_S2:.4f}")
    print(f"是否满足次模性质: {is_submodular}")
    
    return {
        'x': x,
        'y': y,
        'S1': S1,
        'S2': S2,
        'util_S1': util_S1,
        'util_S2': util_S2,
        'util_S1_0': util_S1_0,
        'util_S2_0': util_S2_0,
        'marginal_S1': marginal_S1,
        'marginal_S2': marginal_S2,
        'is_submodular': is_submodular,
        'marginal_difference': marginal_S1 - marginal_S2
    }

def main():
    """
    主函数：生成实例并分析次模性质
    """
    print("=== 阻塞作业车间调度问题的次模性质分析 ===\n")
    
    # 设置随机种子

    
    # 生成5*5实例
    print("生成5*5实例...")
    processing_times, machine_sequences = uni_instance_gen(n_j=7, n_m=7, low=1, high=100)
    
    print("实例数据:")
    print("加工时间矩阵:")
    print(processing_times)
    print("\n机器序列矩阵:")
    print(machine_sequences)
    
    # 生成多个随机数X进行测试
    num_tests = 10000
    results = []
    
    for test_id in range(num_tests):
        print(f"\n{'='*50}")
        print(f"测试 {test_id + 1}/{num_tests}")
        print(f"{'='*50}")
        
        # 随机生成x和y，确保y > x，且不超过实例的工件数量
        x = random.randint(1, 5)  # x从1到3
        y = random.randint(x+1, 6)   # y从x+1到4，确保y > x且不超过工件4
        print(f"随机数 x = {x}, y = {y}")
        
        # 分析次模性质
        result = analyze_submodular_property((processing_times, machine_sequences), x, y)
        results.append(result)
    
    # 汇总结果
    print(f"\n{'='*60}")
    print("汇总结果")
    print(f"{'='*60}")
    
    submodular_count = sum(1 for r in results if r['is_submodular'])
    total_tests = len(results)
    
    print(f"总测试次数: {total_tests}")
    print(f"满足次模性质的次数: {submodular_count}")
    print(f"次模性质满足率: {submodular_count/total_tests*100:.1f}%")
    
    
    # 计算平均边际差值
    avg_marginal_diff = np.mean([r['marginal_difference'] for r in results])
    print(f"\n平均边际贡献差值: {avg_marginal_diff:.4f}")
    
    if avg_marginal_diff >= 0:
        print("结论: 平均而言，该问题表现出次模性质")
    else:
        print("结论: 平均而言，该问题不表现出次模性质")

if __name__ == "__main__":
    main()
