Class: Legion::Extensions::Agentic::Homeostasis::TemporalDiscounting::Helpers::DiscountingEngine

Inherits:
Object
  • Object
show all
Defined in:
lib/legion/extensions/agentic/homeostasis/temporal_discounting/helpers/discounting_engine.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeDiscountingEngine

Returns a new instance of DiscountingEngine.



12
13
14
15
# File 'lib/legion/extensions/agentic/homeostasis/temporal_discounting/helpers/discounting_engine.rb', line 12

def initialize
  @rewards      = {}
  @domain_rates = {}
end

Instance Attribute Details

#domain_ratesObject (readonly)

Returns the value of attribute domain_rates.



10
11
12
# File 'lib/legion/extensions/agentic/homeostasis/temporal_discounting/helpers/discounting_engine.rb', line 10

def domain_rates
  @domain_rates
end

#rewardsObject (readonly)

Returns the value of attribute rewards.



10
11
12
# File 'lib/legion/extensions/agentic/homeostasis/temporal_discounting/helpers/discounting_engine.rb', line 10

def rewards
  @rewards
end

Instance Method Details

#compare_rewards(reward_a_id:, reward_b_id:) ⇒ Object



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/legion/extensions/agentic/homeostasis/temporal_discounting/helpers/discounting_engine.rb', line 25

def compare_rewards(reward_a_id:, reward_b_id:)
  a = @rewards.fetch(reward_a_id, nil)
  b = @rewards.fetch(reward_b_id, nil)
  return { error: :not_found, missing: missing_ids(reward_a_id, reward_b_id) } if a.nil? || b.nil?

  delta = (a.subjective_value - b.subjective_value).round(10)
  preferred = if delta.positive?
                reward_a_id
              elsif delta.negative?
                reward_b_id
              else
                :tied
              end

  {
    preferred:      preferred,
    delta:          delta.abs,
    reward_a_value: a.subjective_value,
    reward_b_value: b.subjective_value
  }
end

#create_reward(label:, amount:, delay:, domain: :general, discount_rate: nil) ⇒ Object



17
18
19
20
21
22
23
# File 'lib/legion/extensions/agentic/homeostasis/temporal_discounting/helpers/discounting_engine.rb', line 17

def create_reward(label:, amount:, delay:, domain: :general, discount_rate: nil)
  rate = discount_rate || get_domain_rate(domain)
  reward = Reward.new(label: label, amount: amount, delay: delay, domain: domain, discount_rate: rate)
  @rewards[reward.id] = reward
  @rewards.shift while @rewards.size > MAX_REWARDS
  reward
end

#get_domain_rate(domain) ⇒ Object



59
60
61
# File 'lib/legion/extensions/agentic/homeostasis/temporal_discounting/helpers/discounting_engine.rb', line 59

def get_domain_rate(domain)
  @domain_rates.fetch(domain, DEFAULT_DISCOUNT_RATE)
end

#immediate_vs_delayed(immediate_amount:, delayed_amount:, delay:, domain: :general) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/legion/extensions/agentic/homeostasis/temporal_discounting/helpers/discounting_engine.rb', line 63

def immediate_vs_delayed(immediate_amount:, delayed_amount:, delay:, domain: :general)
  k = get_domain_rate(domain)
  delayed_sv = (delayed_amount / (1.0 + (k * delay.to_f))).round(10)
  immediate_sv = immediate_amount.to_f.clamp(0.0, 1.0).round(10)

  preferred = immediate_sv >= delayed_sv ? :immediate : :delayed

  {
    preferred:       preferred,
    immediate_value: immediate_sv,
    delayed_value:   delayed_sv,
    delta:           (immediate_sv - delayed_sv).abs.round(10),
    discount_rate:   k
  }
end

#most_valuable(limit: 5) ⇒ Object



117
118
119
120
121
# File 'lib/legion/extensions/agentic/homeostasis/temporal_discounting/helpers/discounting_engine.rb', line 117

def most_valuable(limit: 5)
  @rewards.values
          .sort_by { |r| -r.subjective_value }
          .first(limit)
end

#optimal_delay(reward_id:, min_value: 0.5) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/legion/extensions/agentic/homeostasis/temporal_discounting/helpers/discounting_engine.rb', line 79

def optimal_delay(reward_id:, min_value: 0.5)
  reward = @rewards.fetch(reward_id, nil)
  return { error: :not_found } if reward.nil?

  k = reward.discount_rate
  a = reward.amount
  return { error: :threshold_too_high } if min_value > a

  max_delay = ((a / min_value) - 1.0) / k
  {
    reward_id:     reward_id,
    max_delay:     max_delay.round(10),
    min_value:     min_value,
    amount:        a,
    discount_rate: k
  }
end

#patience_reportObject



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/legion/extensions/agentic/homeostasis/temporal_discounting/helpers/discounting_engine.rb', line 97

def patience_report
  return empty_patience_report if @rewards.empty?

  values        = @rewards.values
  avg_rate      = (values.sum(&:discount_rate) / values.size).round(10)
  avg_sv        = (values.sum(&:subjective_value) / values.size).round(10)
  distribution  = build_impulsivity_distribution(values)

  {
    total_rewards:            @rewards.size,
    avg_discount_rate:        avg_rate,
    avg_subjective_value:     avg_sv,
    impulsivity_distribution: distribution
  }
end

#prune_expired(min_value: 0.05) ⇒ Object



123
124
125
126
127
# File 'lib/legion/extensions/agentic/homeostasis/temporal_discounting/helpers/discounting_engine.rb', line 123

def prune_expired(min_value: 0.05)
  before = @rewards.size
  @rewards.reject! { |_, r| r.subjective_value < min_value }
  { pruned: before - @rewards.size, remaining: @rewards.size }
end

#rewards_by_domain(domain:) ⇒ Object



113
114
115
# File 'lib/legion/extensions/agentic/homeostasis/temporal_discounting/helpers/discounting_engine.rb', line 113

def rewards_by_domain(domain:)
  @rewards.values.select { |r| r.domain == domain }
end

#set_domain_rate(domain:, rate:) ⇒ Object



55
56
57
# File 'lib/legion/extensions/agentic/homeostasis/temporal_discounting/helpers/discounting_engine.rb', line 55

def set_domain_rate(domain:, rate:)
  @domain_rates[domain] = rate.clamp(MIN_DISCOUNT_RATE, MAX_DISCOUNT_RATE)
end

#to_hObject



129
130
131
132
133
134
135
# File 'lib/legion/extensions/agentic/homeostasis/temporal_discounting/helpers/discounting_engine.rb', line 129

def to_h
  {
    total_rewards: @rewards.size,
    domain_rates:  @domain_rates,
    patience:      patience_report
  }
end

#worth_waiting_for?(reward_id:, threshold: 0.3) ⇒ Boolean

Returns:

  • (Boolean)


47
48
49
50
51
52
53
# File 'lib/legion/extensions/agentic/homeostasis/temporal_discounting/helpers/discounting_engine.rb', line 47

def worth_waiting_for?(reward_id:, threshold: 0.3)
  reward = @rewards.fetch(reward_id, nil)
  return { error: :not_found } if reward.nil?

  { reward_id: reward_id, worth_waiting: reward.worth_waiting?(threshold: threshold),
    subjective_value: reward.subjective_value, threshold: threshold }
end