lex-embodied-simulation 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8c0a42550d81eb660131739e12d4413bac5aaf0a68392880c008568a231e3bfa
4
+ data.tar.gz: e38b1c187179a9a0de618c25fa28af58bf095cf8fd5906ed977e0066fb5eb807
5
+ SHA512:
6
+ metadata.gz: 28c09ac49bc090736f6bb3696a3c555f929b1d6130ffbc9fb16a9af85d3fbb17e96f4df334810b32208347d3969f8ee5f46f2e17c53bdebff0821ab32cd53e71
7
+ data.tar.gz: c40107b484e1bdd2ad77620ce8b9e9edd4a46d0cda1ac5c38b33439b4537b7eecda2df36b01a85a25e5f4ee97eb00e9277e2e35991cdd1858bac3b01b99be0cf
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module EmbodiedSimulation
6
+ class Client
7
+ include Runners::EmbodiedSimulation
8
+
9
+ def initialize(engine: nil)
10
+ @engine = engine || Helpers::SimulationEngine.new
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module EmbodiedSimulation
6
+ module Helpers
7
+ module Constants
8
+ MAX_SIMULATIONS = 30
9
+ MAX_STEPS_PER_SIM = 20
10
+ MAX_HISTORY = 200
11
+ MAX_SCENARIOS = 50
12
+
13
+ DEFAULT_FIDELITY = 0.5
14
+ FIDELITY_FLOOR = 0.1
15
+ FIDELITY_DECAY = 0.01
16
+ FIDELITY_BOOST = 0.05
17
+
18
+ OUTCOME_CONFIDENCE_FLOOR = 0.05
19
+ CALIBRATION_ALPHA = 0.1
20
+
21
+ SIMULATION_TYPES = %i[
22
+ action_rehearsal counterfactual empathic
23
+ predictive exploratory defensive
24
+ ].freeze
25
+
26
+ SIMULATION_STATES = %i[
27
+ pending running completed failed aborted
28
+ ].freeze
29
+
30
+ OUTCOME_VALENCES = %i[
31
+ positive negative neutral ambiguous
32
+ ].freeze
33
+
34
+ FIDELITY_LABELS = {
35
+ (0.8..) => :vivid,
36
+ (0.6...0.8) => :clear,
37
+ (0.4...0.6) => :hazy,
38
+ (0.2...0.4) => :vague,
39
+ (..0.2) => :phantom
40
+ }.freeze
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module EmbodiedSimulation
6
+ module Helpers
7
+ class Simulation
8
+ include Constants
9
+
10
+ attr_reader :id, :simulation_type, :domain, :initial_state, :steps,
11
+ :state, :outcome_valence, :fidelity, :created_at
12
+
13
+ def initialize(id:, simulation_type:, domain:, initial_state:, fidelity: DEFAULT_FIDELITY)
14
+ raise ArgumentError, "invalid type: #{simulation_type}" unless SIMULATION_TYPES.include?(simulation_type)
15
+
16
+ @id = id
17
+ @simulation_type = simulation_type
18
+ @domain = domain
19
+ @initial_state = initial_state
20
+ @steps = []
21
+ @state = :pending
22
+ @outcome_valence = nil
23
+ @fidelity = fidelity.to_f.clamp(FIDELITY_FLOOR, 1.0)
24
+ @created_at = Time.now.utc
25
+ end
26
+
27
+ def add_step(action:, expected_state:, confidence: DEFAULT_FIDELITY, somatic_signal: 0.0)
28
+ return nil if @steps.size >= MAX_STEPS_PER_SIM
29
+ return nil unless %i[pending running].include?(@state)
30
+
31
+ @state = :running if @state == :pending
32
+ step = SimulationStep.new(
33
+ index: @steps.size, action: action, expected_state: expected_state,
34
+ confidence: confidence, somatic_signal: somatic_signal
35
+ )
36
+ @steps << step
37
+ step
38
+ end
39
+
40
+ def complete(valence:)
41
+ return nil unless OUTCOME_VALENCES.include?(valence)
42
+ return nil unless %i[pending running].include?(@state)
43
+
44
+ @state = :completed
45
+ @outcome_valence = valence
46
+ end
47
+
48
+ def abort_simulation
49
+ return nil if @state == :completed
50
+
51
+ @state = :aborted
52
+ end
53
+
54
+ def fail_simulation
55
+ return nil if @state == :completed
56
+
57
+ @state = :failed
58
+ end
59
+
60
+ def running?
61
+ @state == :running
62
+ end
63
+
64
+ def completed?
65
+ @state == :completed
66
+ end
67
+
68
+ def successful?
69
+ @state == :completed && @outcome_valence == :positive
70
+ end
71
+
72
+ def aggregate_somatic
73
+ return 0.0 if @steps.empty?
74
+
75
+ @steps.sum(&:somatic_signal) / @steps.size
76
+ end
77
+
78
+ def aggregate_confidence
79
+ return 0.0 if @steps.empty?
80
+
81
+ @steps.sum(&:confidence) / @steps.size
82
+ end
83
+
84
+ def negative_signals?
85
+ @steps.any?(&:negative_signal?)
86
+ end
87
+
88
+ def step_count
89
+ @steps.size
90
+ end
91
+
92
+ def counterfactual?
93
+ @simulation_type == :counterfactual
94
+ end
95
+
96
+ def rehearsal?
97
+ @simulation_type == :action_rehearsal
98
+ end
99
+
100
+ def empathic?
101
+ @simulation_type == :empathic
102
+ end
103
+
104
+ def fidelity_label
105
+ FIDELITY_LABELS.each { |range, lbl| return lbl if range.cover?(@fidelity) }
106
+ :phantom
107
+ end
108
+
109
+ def decay_fidelity
110
+ @fidelity = [@fidelity - FIDELITY_DECAY, FIDELITY_FLOOR].max
111
+ end
112
+
113
+ def to_h
114
+ {
115
+ id: @id,
116
+ simulation_type: @simulation_type,
117
+ domain: @domain,
118
+ state: @state,
119
+ step_count: @steps.size,
120
+ outcome_valence: @outcome_valence,
121
+ fidelity: @fidelity.round(4),
122
+ fidelity_label: fidelity_label,
123
+ somatic_avg: aggregate_somatic.round(4),
124
+ confidence_avg: aggregate_confidence.round(4),
125
+ created_at: @created_at
126
+ }
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module EmbodiedSimulation
6
+ module Helpers
7
+ class SimulationEngine
8
+ include Constants
9
+
10
+ attr_reader :simulations, :history, :calibration
11
+
12
+ def initialize
13
+ @simulations = {}
14
+ @counter = 0
15
+ @history = []
16
+ @calibration = 0.5
17
+ end
18
+
19
+ def create_simulation(simulation_type:, domain:, initial_state:, fidelity: DEFAULT_FIDELITY)
20
+ return nil unless SIMULATION_TYPES.include?(simulation_type)
21
+ return nil if @simulations.size >= MAX_SIMULATIONS
22
+
23
+ @counter += 1
24
+ sim_id = :"sim_#{@counter}"
25
+ sim = Simulation.new(
26
+ id: sim_id, simulation_type: simulation_type,
27
+ domain: domain, initial_state: initial_state, fidelity: fidelity
28
+ )
29
+ @simulations[sim_id] = sim
30
+ sim
31
+ end
32
+
33
+ def add_step(sim_id:, action:, expected_state:, confidence: DEFAULT_FIDELITY, somatic_signal: 0.0)
34
+ sim = @simulations[sim_id]
35
+ return nil unless sim
36
+
37
+ sim.add_step(action: action, expected_state: expected_state,
38
+ confidence: confidence, somatic_signal: somatic_signal)
39
+ end
40
+
41
+ def complete_simulation(sim_id:, valence:)
42
+ sim = @simulations[sim_id]
43
+ return nil unless sim
44
+
45
+ result = sim.complete(valence: valence)
46
+ record_completion(sim) if result
47
+ result
48
+ end
49
+
50
+ def abort_simulation(sim_id:)
51
+ sim = @simulations[sim_id]
52
+ return nil unless sim
53
+
54
+ sim.abort_simulation
55
+ end
56
+
57
+ def rehearse(domain:, action_sequence:, initial_state: {})
58
+ sim = create_simulation(
59
+ simulation_type: :action_rehearsal, domain: domain,
60
+ initial_state: initial_state
61
+ )
62
+ return nil unless sim
63
+
64
+ run_sequence(sim, action_sequence)
65
+ sim
66
+ end
67
+
68
+ def counterfactual(domain:, actual_outcome:, alternative_actions:, initial_state: {})
69
+ sim = create_simulation(
70
+ simulation_type: :counterfactual, domain: domain,
71
+ initial_state: initial_state.merge(actual_outcome: actual_outcome)
72
+ )
73
+ return nil unless sim
74
+
75
+ run_sequence(sim, alternative_actions)
76
+ sim
77
+ end
78
+
79
+ def evaluate_simulation(sim_id:)
80
+ sim = @simulations[sim_id]
81
+ return nil unless sim
82
+
83
+ build_evaluation(sim)
84
+ end
85
+
86
+ def compare_simulations(*sim_ids)
87
+ sims = sim_ids.filter_map { |id| @simulations[id] }
88
+ return [] if sims.empty?
89
+
90
+ sims.sort_by { |s| -(s.aggregate_somatic + s.aggregate_confidence) }.map(&:to_h)
91
+ end
92
+
93
+ def simulations_for(domain:)
94
+ @simulations.values.select { |s| s.domain == domain }.map(&:to_h)
95
+ end
96
+
97
+ def active_simulations
98
+ @simulations.values.select(&:running?).map(&:to_h)
99
+ end
100
+
101
+ def completed_simulations
102
+ @simulations.values.select(&:completed?).map(&:to_h)
103
+ end
104
+
105
+ def calibrate(sim_id:, actual_valence:)
106
+ sim = @simulations[sim_id]
107
+ return nil unless sim&.completed?
108
+
109
+ match = sim.outcome_valence == actual_valence ? 1.0 : 0.0
110
+ @calibration += CALIBRATION_ALPHA * (match - @calibration)
111
+ @calibration = @calibration.clamp(0.0, 1.0)
112
+ sim.fidelity_label
113
+ end
114
+
115
+ def decay_all
116
+ @simulations.each_value(&:decay_fidelity)
117
+ @simulations.reject! { |_, s| s.fidelity <= FIDELITY_FLOOR && s.completed? }
118
+ end
119
+
120
+ def to_h
121
+ {
122
+ simulation_count: @simulations.size,
123
+ active_count: @simulations.values.count(&:running?),
124
+ completed_count: @simulations.values.count(&:completed?),
125
+ calibration: @calibration.round(4),
126
+ history_size: @history.size
127
+ }
128
+ end
129
+
130
+ private
131
+
132
+ def run_sequence(sim, actions)
133
+ actions.each_with_index do |act, _i|
134
+ sim.add_step(
135
+ action: act[:action] || act,
136
+ expected_state: act[:expected_state] || {},
137
+ confidence: act[:confidence] || DEFAULT_FIDELITY,
138
+ somatic_signal: act[:somatic_signal] || 0.0
139
+ )
140
+ end
141
+ end
142
+
143
+ def record_completion(sim)
144
+ @history << {
145
+ id: sim.id, type: sim.simulation_type, domain: sim.domain,
146
+ valence: sim.outcome_valence, steps: sim.step_count, at: Time.now.utc
147
+ }
148
+ @history.shift while @history.size > MAX_HISTORY
149
+ end
150
+
151
+ def build_evaluation(sim)
152
+ {
153
+ id: sim.id,
154
+ state: sim.state,
155
+ recommendation: recommend(sim),
156
+ somatic_avg: sim.aggregate_somatic.round(4),
157
+ confidence_avg: sim.aggregate_confidence.round(4),
158
+ has_warnings: sim.negative_signals?,
159
+ fidelity: sim.fidelity.round(4),
160
+ step_count: sim.step_count
161
+ }
162
+ end
163
+
164
+ def recommend(sim)
165
+ return :insufficient_data if sim.steps.empty?
166
+ return :abort if sim.aggregate_somatic < -0.5
167
+ return :proceed_with_caution if sim.negative_signals?
168
+ return :proceed if sim.aggregate_somatic >= 0.0 && sim.aggregate_confidence >= 0.5
169
+
170
+ :reconsider
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module EmbodiedSimulation
6
+ module Helpers
7
+ class SimulationStep
8
+ include Constants
9
+
10
+ attr_reader :index, :action, :expected_state, :confidence, :somatic_signal
11
+
12
+ def initialize(index:, action:, expected_state:, confidence: DEFAULT_FIDELITY, somatic_signal: 0.0)
13
+ @index = index
14
+ @action = action
15
+ @expected_state = expected_state
16
+ @confidence = confidence.to_f.clamp(0.0, 1.0)
17
+ @somatic_signal = somatic_signal.to_f.clamp(-1.0, 1.0)
18
+ end
19
+
20
+ def positive_signal?
21
+ @somatic_signal > 0.2
22
+ end
23
+
24
+ def negative_signal?
25
+ @somatic_signal < -0.2
26
+ end
27
+
28
+ def high_confidence?
29
+ @confidence >= 0.7
30
+ end
31
+
32
+ def to_h
33
+ {
34
+ index: @index,
35
+ action: @action,
36
+ expected_state: @expected_state,
37
+ confidence: @confidence.round(4),
38
+ somatic_signal: @somatic_signal.round(4)
39
+ }
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module EmbodiedSimulation
6
+ module Runners
7
+ module EmbodiedSimulation
8
+ include Helpers::Constants
9
+
10
+ include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
11
+
12
+ def create_simulation(simulation_type:, domain:, initial_state: {}, fidelity: DEFAULT_FIDELITY, **)
13
+ sim = engine.create_simulation(
14
+ simulation_type: simulation_type, domain: domain,
15
+ initial_state: initial_state, fidelity: fidelity
16
+ )
17
+ return { success: false, reason: :limit_or_invalid_type } unless sim
18
+
19
+ { success: true, simulation_id: sim.id, simulation_type: sim.simulation_type, domain: sim.domain }
20
+ end
21
+
22
+ def add_simulation_step(sim_id:, action:, expected_state: {}, confidence: DEFAULT_FIDELITY,
23
+ somatic_signal: 0.0, **)
24
+ step = engine.add_step(
25
+ sim_id: sim_id, action: action, expected_state: expected_state,
26
+ confidence: confidence, somatic_signal: somatic_signal
27
+ )
28
+ return { success: false, reason: :not_found_or_full } unless step
29
+
30
+ { success: true, step_index: step.index, action: step.action }
31
+ end
32
+
33
+ def complete_simulation(sim_id:, valence:, **)
34
+ result = engine.complete_simulation(sim_id: sim_id, valence: valence)
35
+ return { success: false, reason: :not_found_or_invalid } unless result
36
+
37
+ { success: true, simulation_id: sim_id, valence: valence }
38
+ end
39
+
40
+ def rehearse_action(domain:, action_sequence:, initial_state: {}, **)
41
+ sim = engine.rehearse(domain: domain, action_sequence: action_sequence, initial_state: initial_state)
42
+ return { success: false, reason: :limit_reached } unless sim
43
+
44
+ { success: true, simulation_id: sim.id, step_count: sim.step_count,
45
+ somatic_avg: sim.aggregate_somatic.round(4) }
46
+ end
47
+
48
+ def run_counterfactual(domain:, actual_outcome:, alternative_actions:, initial_state: {}, **)
49
+ sim = engine.counterfactual(
50
+ domain: domain, actual_outcome: actual_outcome,
51
+ alternative_actions: alternative_actions, initial_state: initial_state
52
+ )
53
+ return { success: false, reason: :limit_reached } unless sim
54
+
55
+ { success: true, simulation_id: sim.id, step_count: sim.step_count }
56
+ end
57
+
58
+ def evaluate_simulation(sim_id:, **)
59
+ result = engine.evaluate_simulation(sim_id: sim_id)
60
+ return { success: false, reason: :not_found } unless result
61
+
62
+ { success: true }.merge(result)
63
+ end
64
+
65
+ def compare_simulations(sim_ids:, **)
66
+ ranked = engine.compare_simulations(*sim_ids)
67
+ { success: true, ranked: ranked, count: ranked.size }
68
+ end
69
+
70
+ def calibrate_simulation(sim_id:, actual_valence:, **)
71
+ label = engine.calibrate(sim_id: sim_id, actual_valence: actual_valence)
72
+ return { success: false, reason: :not_found_or_incomplete } unless label
73
+
74
+ { success: true, calibration: engine.calibration.round(4), fidelity_label: label }
75
+ end
76
+
77
+ def update_embodied_simulation(**)
78
+ engine.decay_all
79
+ { success: true }.merge(engine.to_h)
80
+ end
81
+
82
+ def embodied_simulation_stats(**)
83
+ { success: true }.merge(engine.to_h)
84
+ end
85
+
86
+ private
87
+
88
+ def engine
89
+ @engine ||= Helpers::SimulationEngine.new
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module EmbodiedSimulation
6
+ VERSION = '0.1.0'
7
+ end
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lex-embodied-simulation
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Esity
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: legion-gaia
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ description: Embodied simulation engine for LegionIO — mental rehearsal, action simulation,
27
+ and counterfactual reasoning
28
+ email:
29
+ - matthewdiverson@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - lib/legion/extensions/embodied_simulation/client.rb
35
+ - lib/legion/extensions/embodied_simulation/helpers/constants.rb
36
+ - lib/legion/extensions/embodied_simulation/helpers/simulation.rb
37
+ - lib/legion/extensions/embodied_simulation/helpers/simulation_engine.rb
38
+ - lib/legion/extensions/embodied_simulation/helpers/simulation_step.rb
39
+ - lib/legion/extensions/embodied_simulation/runners/embodied_simulation.rb
40
+ - lib/legion/extensions/embodied_simulation/version.rb
41
+ homepage: https://github.com/LegionIO/lex-embodied-simulation
42
+ licenses:
43
+ - MIT
44
+ metadata:
45
+ homepage_uri: https://github.com/LegionIO/lex-embodied-simulation
46
+ source_code_uri: https://github.com/LegionIO/lex-embodied-simulation
47
+ documentation_uri: https://github.com/LegionIO/lex-embodied-simulation/blob/master/README.md
48
+ changelog_uri: https://github.com/LegionIO/lex-embodied-simulation/blob/master/CHANGELOG.md
49
+ bug_tracker_uri: https://github.com/LegionIO/lex-embodied-simulation/issues
50
+ rubygems_mfa_required: 'true'
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '3.4'
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubygems_version: 3.6.9
66
+ specification_version: 4
67
+ summary: Barsalou's grounded cognition for LegionIO
68
+ test_files: []