File size: 8,113 Bytes
bf78963
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# Quantum Agent-Task Optimization (QATO) Simulation
# This script simulates how a quantum annealer would solve the optimal task
# assignment problem for the "Society of Minds" architecture.

# We use D-Wave's 'dimod' library, which provides tools for working with
# QUBO (Quadratic Unconstrained Binary Optimization) models, the native
# input format for quantum annealers.
# You can install it via pip: pip install dimod numpy

import numpy as np
import dimod
import time

def create_society_of_minds():
    """Defines the agents and their specialized roles for Phase I: The Town."""
    # As per the spec, we create 30 agents with specialized roles.
    # To make it interesting, we'll create a few of each type.
    roles = {
        "Data_Analyst": 5,
        "Creative_Writer": 3,
        "Debugger": 4,
        "Network_Security_Specialist": 5,
        "System_Administrator": 4,
        "UX_Designer": 3,
        "Project_Manager": 2,
        "Hardware_Technician": 4
    }
    agents = []
    for role, count in roles.items():
        for i in range(1, count + 1):
            agents.append(f"{role}_{i}")
    return agents

def create_job_board():
    """Defines the tasks and their required skills for the Task Marketplace."""
    # Tasks are designed to map to the agent specializations.
    tasks = {
        "Analyze_Q3_Login_Anomalies": "Data_Analyst",
        "Draft_Internal_Security_Bulletin": "Creative_Writer",
        "Patch_Kernel_Vulnerability_CVE-2024-XXXX": "Debugger",
        "Configure_New_Firewall_Rules": "Network_Security_Specialist",
        "Migrate_Database_Server": "System_Administrator",
        "Design_New_Dashboard_Mockup": "UX_Designer",
        "Coordinate_Software_Release_v2.1": "Project_Manager",
        "Replace_Failing_RAID_Controller": "Hardware_Technician",
        "Identify_Phishing_Campaign_Patterns": "Data_Analyst",
        "Review_Code_For_Buffer_Overflows": "Debugger",
        "Audit_VPN_Access_Logs": "Network_Security_Specialist",
        "Write_User_Documentation_For_New_App": "Creative_Writer",
    }
    return tasks

def calculate_suitability(agents, tasks):
    """
    Creates a suitability matrix (S).
    S[i, j] is a score representing how suitable agent i is for task j.
    A higher score is better.
    """
    num_agents = len(agents)
    num_task_keys = len(tasks.keys())
    task_list = list(tasks.keys())
    
    # Initialize a matrix of zeros
    suitability_matrix = np.zeros((num_agents, num_task_keys))

    for i, agent_name in enumerate(agents):
        agent_role = agent_name.split('_')[0]
        for j, task_name in enumerate(task_list):
            required_role = tasks[task_name]
            if agent_role == required_role:
                # High suitability if the role matches perfectly
                suitability_matrix[i, j] = 10
            else:
                # Low suitability otherwise
                suitability_matrix[i, j] = 1
                
    return suitability_matrix

def build_qubo(suitability_matrix):
    """
    Constructs the QUBO model from the suitability matrix.
    The goal is to MAXIMIZE the total suitability score. Quantum annealers
    MINIMIZE, so we formulate the problem as minimizing the NEGATIVE of the score.
    """
    num_agents, num_tasks = suitability_matrix.shape
    
    # Initialize the QUBO dictionary
    Q = {}

    # --- Objective Function ---
    # We want to maximize sum(S_ij * x_ij), where x_ij is a binary variable
    # that is 1 if agent i is assigned task j.
    # This is equivalent to minimizing sum(-S_ij * x_ij).
    # In QUBO, linear terms go on the diagonal.
    for i in range(num_agents):
        for j in range(num_tasks):
            # The variable is x_ij, which we can represent as a tuple (i, j)
            variable = (i, j)
            Q[(variable, variable)] = -suitability_matrix[i, j]

    # --- Constraints ---
    # We need to add penalties to ensure the solution is valid.
    # A large penalty term (gamma) ensures constraints are met.
    gamma = 20 # This value should be larger than any possible suitability score

    # Constraint 1: Each task must be assigned to exactly one agent.
    # For each task j, sum(x_ij for i in agents) == 1
    # The penalty term for this is gamma * (sum(x_ij) - 1)^2
    for j in range(num_tasks):
        for i in range(num_agents):
            # Linear part of the squared term
            var1 = (i, j)
            if (var1, var1) in Q:
                Q[(var1, var1)] += gamma * -2
            else:
                Q[(var1, var1)] = gamma * -2
            
            # Quadratic part of the squared term
            for k in range(i + 1, num_agents):
                var2 = (k, j)
                if (var1, var2) in Q:
                    Q[(var1, var2)] += gamma * 2
                else:
                    Q[(var1, var2)] = gamma * 2

    # Constraint 2: Each agent can be assigned at most one task.
    # For each agent i, sum(x_ij for j in tasks) <= 1
    # The penalty term for this is gamma * (sum(x_ij) * (sum(x_ij) - 1))
    # which simplifies to gamma * sum(x_ij * x_ik for j < k)
    for i in range(num_agents):
        for j in range(num_tasks):
            for k in range(j + 1, num_tasks):
                var1 = (i, j)
                var2 = (i, k)
                if (var1, var2) in Q:
                    Q[(var1, var2)] += gamma * 2
                else:
                    Q[(var1, var2)] = gamma * 2
                    
    return Q

def solve_and_interpret(qubo, agents, tasks, S):
    """Solves the QUBO using a simulated annealer and interprets the result."""
    task_list = list(tasks.keys())
    num_agents = len(agents)
    num_tasks = len(task_list)

    # Use dimod's classical solver that simulates the behavior of a quantum annealer
    sampler = dimod.SimulatedAnnealingSampler()
    
    # num_reads is like running the experiment multiple times to find the best result.
    # We've reduced this from 100 to 10 to significantly speed up the simulation.
    num_reads = 10
    
    print(f"** Solving with {num_reads} reads... (this may take a moment) **")
    start_time = time.time()
    response = sampler.sample_qubo(qubo, num_reads=num_reads)
    end_time = time.time()
    print(f"** Solving complete in {end_time - start_time:.2f} seconds. **\n")

    
    # The best solution is the one with the lowest energy
    best_solution = response.first.sample
    
    print("--- Optimal Task Assignment Found ---")
    total_suitability = 0
    assignments_found = 0
    
    for i in range(num_agents):
        for j in range(num_tasks):
            if best_solution.get((i, j), 0) == 1:
                agent_name = agents[i]
                task_name = task_list[j]
                suitability_score = S[i, j]
                total_suitability += suitability_score
                print(f"Assign -> Agent: {agent_name:<30} | Task: {task_name:<40} | Suitability: {suitability_score:.0f}")
                assignments_found += 1
    
    if assignments_found == 0:
        print("\n!! No valid assignments were found. The model may be too constrained or the penalty 'gamma' may need adjustment.")
    
    print("\n--- Total Suitability Score ---")
    print(f"The total score for this assignment is: {total_suitability:.0f}")


# --- Main Execution ---
if __name__ == "__main__":
    print("Initializing Project Chimera: Phase I (The Town)...")
    
    # 1. Create the agents and tasks
    agents = create_society_of_minds()
    tasks = create_job_board()
    print(f"Created {len(agents)} agents and {len(tasks)} tasks.\n")

    # 2. Define the problem by calculating suitability
    print("Calculating suitability matrix...")
    S = calculate_suitability(agents, tasks)
    
    # 3. Formulate the problem for the quantum computer (build the QUBO)
    print("Building QUBO model for quantum annealer...")
    qubo_model = build_qubo(S)
    
    # 4. Solve on the simulated quantum annealer and interpret the results
    print("Sending to simulated quantum annealer to find optimal assignment...\n")
    solve_and_interpret(qubo_model, agents, tasks, S)