From 83d9bb25521d8a0d7cbe22e10630ff2a8e1bbd59 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Wed, 28 Jan 2026 14:04:57 +0100 Subject: [PATCH] chore: properly developing --- engine/lib/behavior.py | 48 ++++++++++++++++++++++++++++++++ engine/lib/demand.py | 40 ++++++++++++++++++++++++++ engine/main.py | 0 sim/rl/behavior_loader/models.py | 4 +++ 4 files changed, 92 insertions(+) create mode 100644 engine/lib/behavior.py create mode 100644 engine/lib/demand.py create mode 100644 engine/main.py diff --git a/engine/lib/behavior.py b/engine/lib/behavior.py new file mode 100644 index 0000000..69b6649 --- /dev/null +++ b/engine/lib/behavior.py @@ -0,0 +1,48 @@ +from sim.rl.behavior_loader.models import BehaviorModel, AgentBehaviorModel, aggregate_event_transitions +import pandas as pd +import numpy as np +from .demand import generate_demand + +base_dir = "/home/velocitatem/Documents/Projects/PHANTOM/experiments" +human_dir, agent_dir = f"{base_dir}/collected_data/", f"{base_dir}/agents/collected_data/" + + +def adjust_behavior_to_condition(condition, transition_matrix): + # transition matrix just maps probability of eventA to eventB + # we enhance this that eventA-productI to eventB-productJ... based on the condition of interest + # this is to simulate the impact of demand onto the behavior + # NxN -> (N*(P + 1))x(N*(P + 1)) where P is number of products + new_transitions = transition_matrix.copy() + for col in new_transitions.columns: + for product in range(len(condition)): + # adjust the transition probability based on the demand condition + newname = f"{col}_product{product}" + new_transitions[newname] = new_transitions[col] * (condition[product] / np.sum(condition)) + for row in transition_matrix.index: + for product in range(len(condition)): + newname = f"{row}_product{product}" + new_transitions.loc[newname] = new_transitions.loc[row] * (condition[product] / np.sum(condition)) + + return new_transitions.fillna(0.0) + +def sample_behavior(condition, human=True, max_len=40): + model = BehaviorModel(human_dir) if human else AgentBehaviorModel(agent_dir) + mdp = model.build_MDP() + raw_events = aggregate_event_transitions(mdp) # this gets us transtition between events (blind to products or prices) + # staet: {state: p} is raw_events we needc a matrix a pivot table + events_pivot = pd.DataFrame(raw_events).fillna(0.0) + # now adjust the transition matrix based on the condition to get a more informed transition matrix + adjusted_transitions = adjust_behavior_to_condition(condition, events_pivot) + + trajectory = [np.random.choice(adjusted_transitions.index)] + while len(trajectory) < max_len or 'checkout' in trajectory[-1]: + probs = adjusted_transitions.loc[trajectory[-1]].values + sample = np.random.choice(adjusted_transitions.columns, p=probs/np.sum(probs) if np.sum(probs) > 0 else None) + trajectory.append(sample) + return trajectory + +if __name__ == "__main__": + t=sample_behavior(generate_demand(np.array([10,20,30])), human=True) + print(t) + t=sample_behavior(generate_demand(np.array([10,20,30])), human=False) + print(t) diff --git a/engine/lib/demand.py b/engine/lib/demand.py new file mode 100644 index 0000000..52e28e7 --- /dev/null +++ b/engine/lib/demand.py @@ -0,0 +1,40 @@ +import numpy as np + +def generate_demand(prices): + # assumption 1: each product has an intrinsic valuation drawn from a normal distribution centered at 50 + product_valuations = np.random.normal(loc=50.0, scale=10.0, size=len(prices)) + # assumption 2: demand decreases as price increases, following a simple linear model + demand = np.maximum(0, product_valuations - prices) # demand cannot be negative + demand = demand / np.sum(demand) * 100 # normalize to total demand of 1000 units so demand output is within [0, 100] + return demand + +def estimate_demand(trajectories): + demand_estimate = {} + for traj in trajectories: + for event in traj: + if 'view_product' in event: + product_id = int(event.split('_')[-1].replace('product', '')) + demand_estimate[product_id] = demand_estimate.get(product_id, 0) + 1 + total_views = sum(demand_estimate.values()) + for product_id in demand_estimate: + demand_estimate[product_id] = (demand_estimate[product_id] / total_views) * 100 # normalize to percentage + return demand_estimate + +# Example usage +if __name__ == "__main__": + np.random.seed(42) + prices = np.array([20.0, 35.0, 50.0, 65.0]) + demand = generate_demand(prices) + print("Generated Demand:", demand) + from .behavior import sample_behavior + N, alphat =200, 0.1 + trajectories = [] + for _ in range(int(N*(1 - alphat))): + trajectories.append(sample_behavior(demand, human=True)) + for _ in range(int(N*alphat)): + trajectories.append(sample_behavior(demand, human=False)) + demand_estimate = estimate_demand(trajectories) + print("Estimated Demand from Behavior:", demand_estimate) + delta = {k: demand_estimate.get(k, 0) - demand[i] for i, k in enumerate(range(len(prices)))} + delta = np.mean([np.abs(v) for v in delta.values()]) + print("Demand Delta:", delta) diff --git a/engine/main.py b/engine/main.py new file mode 100644 index 0000000..e69de29 diff --git a/sim/rl/behavior_loader/models.py b/sim/rl/behavior_loader/models.py index 33f83f4..8cc0214 100644 --- a/sim/rl/behavior_loader/models.py +++ b/sim/rl/behavior_loader/models.py @@ -226,6 +226,7 @@ if __name__ == "__main__": agent_model = AgentBehaviorModel(agent_dir) agent_mdp = agent_model.build_MDP() + print(agent_mdp) print(f"AGENT... Built MDP: {agent_mdp['num_states']} states, " f"{sum(len(t) for t in agent_mdp['transitions'].values())} transitions") if not agent_mdp['states']: @@ -234,6 +235,9 @@ if __name__ == "__main__": human_evt = aggregate_event_transitions(human_mdp) agent_evt = aggregate_event_transitions(agent_mdp) + print(agent_evt) + + common = set(human_evt.keys()) & set(agent_evt.keys()) if not common: