AERA
auto_focus.cpp
1 //_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
2 //_/_/
3 //_/_/ AERA
4 //_/_/ Autocatalytic Endogenous Reflective Architecture
5 //_/_/
6 //_/_/ Copyright (c) 2018-2025 Jeff Thompson
7 //_/_/ Copyright (c) 2018-2025 Kristinn R. Thorisson
8 //_/_/ Copyright (c) 2018-2025 Icelandic Institute for Intelligent Machines
9 //_/_/ Copyright (c) 2018 Jacqueline Clare Mallett
10 //_/_/ http://www.iiim.is
11 //_/_/
12 //_/_/ Copyright (c) 2010-2012 Eric Nivel
13 //_/_/ Center for Analysis and Design of Intelligent Agents
14 //_/_/ Reykjavik University, Menntavegur 1, 102 Reykjavik, Iceland
15 //_/_/ http://cadia.ru.is
16 //_/_/
17 //_/_/ Part of this software was developed by Eric Nivel
18 //_/_/ in the HUMANOBS EU research project, which included
19 //_/_/ the following parties:
20 //_/_/
21 //_/_/ Autonomous Systems Laboratory
22 //_/_/ Technical University of Madrid, Spain
23 //_/_/ http://www.aslab.org/
24 //_/_/
25 //_/_/ Communicative Machines
26 //_/_/ Edinburgh, United Kingdom
27 //_/_/ http://www.cmlabs.com/
28 //_/_/
29 //_/_/ Istituto Dalle Molle di Studi sull'Intelligenza Artificiale
30 //_/_/ University of Lugano and SUPSI, Switzerland
31 //_/_/ http://www.idsia.ch/
32 //_/_/
33 //_/_/ Institute of Cognitive Sciences and Technologies
34 //_/_/ Consiglio Nazionale delle Ricerche, Italy
35 //_/_/ http://www.istc.cnr.it/
36 //_/_/
37 //_/_/ Dipartimento di Ingegneria Informatica
38 //_/_/ University of Palermo, Italy
39 //_/_/ http://diid.unipa.it/roboticslab/
40 //_/_/
41 //_/_/
42 //_/_/ --- HUMANOBS Open-Source BSD License, with CADIA Clause v 1.0 ---
43 //_/_/
44 //_/_/ Redistribution and use in source and binary forms, with or without
45 //_/_/ modification, is permitted provided that the following conditions
46 //_/_/ are met:
47 //_/_/ - Redistributions of source code must retain the above copyright
48 //_/_/ and collaboration notice, this list of conditions and the
49 //_/_/ following disclaimer.
50 //_/_/ - Redistributions in binary form must reproduce the above copyright
51 //_/_/ notice, this list of conditions and the following disclaimer
52 //_/_/ in the documentation and/or other materials provided with
53 //_/_/ the distribution.
54 //_/_/
55 //_/_/ - Neither the name of its copyright holders nor the names of its
56 //_/_/ contributors may be used to endorse or promote products
57 //_/_/ derived from this software without specific prior
58 //_/_/ written permission.
59 //_/_/
60 //_/_/ - CADIA Clause: The license granted in and to the software
61 //_/_/ under this agreement is a limited-use license.
62 //_/_/ The software may not be used in furtherance of:
63 //_/_/ (i) intentionally causing bodily injury or severe emotional
64 //_/_/ distress to any person;
65 //_/_/ (ii) invading the personal privacy or violating the human
66 //_/_/ rights of any person; or
67 //_/_/ (iii) committing or preparing for any act of war.
68 //_/_/
69 //_/_/ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
70 //_/_/ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
71 //_/_/ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
72 //_/_/ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
73 //_/_/ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
74 //_/_/ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
75 //_/_/ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
76 //_/_/ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
77 //_/_/ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
78 //_/_/ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
79 //_/_/ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
80 //_/_/ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
81 //_/_/ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
82 //_/_/ OF SUCH DAMAGE.
83 //_/_/
84 //_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
85 
86 #include "mem.h"
87 #include "auto_focus.h"
88 #include "ast_controller.h"
89 
90 using namespace std;
91 using namespace r_code;
92 
93 namespace r_exec {
94 
95 AutoFocusController::AutoFocusController(_View *view) : Controller(view) {
96 
97  // Load arguments: pass_through, acquire_models, decompile_models, list of output groups: 1st must be the primary, 2nd the secondary, then other groups.
98  Code *icpp_pgm = get_object();
99  uint16 arg_set_index = icpp_pgm->code(ICPP_PGM_ARGS).asIndex();
100  uint16 arg_count = icpp_pgm->code(arg_set_index).getAtomCount();
101  uint8 i = 1;
102  pass_through_ = icpp_pgm->code(arg_set_index + i++).asBoolean();
103  ctpx_on_ = icpp_pgm->code(arg_set_index + i++).asBoolean();
104  gtpx_on_ = icpp_pgm->code(arg_set_index + i++).asBoolean();
105  ptpx_on_ = icpp_pgm->code(arg_set_index + i++).asBoolean();
106  trace_injections_ = icpp_pgm->code(arg_set_index + i++).asBoolean();
107  decompile_models_ = icpp_pgm->code(arg_set_index + i).asBoolean();
108  for (uint16 j = i; j < arg_count; ++j)
109  output_groups_.push_back((Group *)icpp_pgm->get_reference(j - i));
110 
111  cross_buffer_.set_thz(_Mem::Get()->get_tpx_time_horizon());
112  cross_buffer_.reserve(CrossBufferInitialSize);
113  auto thz = 2 * ((r_exec::View*)view)->get_host()->get_upr()*Utils::GetBasePeriod(); // thz==2*sampling period.
114  cache_.set_thz(thz);
115  cache_.reserve(CacheInitialSize);
116 
117 #ifdef WITH_DETAIL_OID
118  OUTPUT_LINE(AUTO_FOCUS, "new controller(" << get_detail_oid() << ") for auto_focus " << view->object_->get_oid());
119 #endif
120 }
121 
122 AutoFocusController::~AutoFocusController() {
123 }
124 
125 Code *AutoFocusController::get_core_object() const {
126 
127  return get_object(); // icpp_pgm.
128 }
129 
130 inline void AutoFocusController::inject_input(View *input, uint32 start) {
131 
132  Group *origin = input->get_host();
133  for (uint16 i = start; i < output_groups_.size(); ++i) {
134 
135  Group *output_group = output_groups_[i];
136  View *view = new View(input, true);
137  view->references_[0] = output_group;
138  view->code(VIEW_RES) = Atom::Float(Utils::GetResilience(view->code(VIEW_RES).asFloat(), origin->get_upr(), output_group->get_upr()));
139  _Mem::Get()->inject(view);
140  }
141 }
142 
143 inline void AutoFocusController::inject_input(View *input, _Fact *abstract_input, BindingMap *bm) {
144 
145  View *primary_view = inject_input(input);
146  cross_buffer_.push_back(Input(primary_view, abstract_input, bm));
147 }
148 
149 inline View *AutoFocusController::inject_input(View *input) {
150 
151  _Fact *input_fact = (_Fact *)input->object_;
152 
153  Group *origin = input->get_host();
154  Group *ref_group = output_groups_[0];
155 
156  auto now = Now();
157 
158  View *primary_view;
159  _Fact *copy;
160  switch (input->get_sync()) {
161  case View::SYNC_ONCE: // no copy, morph res; N.B.: cmds are sync_once.
162  for (uint16 i = 0; i < output_groups_.size(); ++i) {
163 
164  Group *output_group = output_groups_[i];
165  View *view = new View(input, true);
166  view->references_[0] = output_group;
167  view->references_[1] = input->references_[0];
168  view->code(VIEW_RES) = Atom::Float(Utils::GetResilience(view->code(VIEW_RES).asFloat(), origin->get_upr(), output_group->get_upr()));
169  _Mem::Get()->inject(view);
170  if (i == 0)
171  primary_view = view;
172  }
173  break;
174  case View::SYNC_PERIODIC: // inject a copy, morph res, add a controller.
175  if (input_fact->is_anti_fact())
176  copy = new AntiFact(input_fact->get_reference(0), ref_group->get_prev_upr_time(now), ref_group->get_next_upr_time(now), 1, 1);
177  else
178  copy = new Fact(input_fact->get_reference(0), ref_group->get_prev_upr_time(now), ref_group->get_next_upr_time(now), 1, 1);
179  for (uint16 i = 0; i < output_groups_.size(); ++i) {
180 
181  Group *output_group = output_groups_[i];
182  View *view = new View(input, true);
183  view->references_[0] = output_group;
184  view->references_[1] = input->references_[0];
185  view->code(VIEW_RES) = Atom::Float(Utils::GetResilience(view->code(VIEW_RES).asFloat(), origin->get_upr(), output_group->get_upr()));
186  view->object_ = copy;
187  _Mem::Get()->inject(view);
188  if (i == 0) {
189 
190  primary_view = view;
191  // Don't use CTPX to model changes in anti-facts.
192  if (ctpx_on_ && input_fact->is_fact())
193  // Set time_to_live to 2 frame periods so that PASTController stays valid during the whole frame when CTPX may build models.
194  _Mem::Get()->inject_null_program(new PASTController(this, view), output_group, 2 * output_group->get_upr()*Utils::GetBasePeriod(), true);
195  }
196  }
197  break;
198  case View::SYNC_HOLD: { // inject a copy, add a controller, sync_once, morph res, after=now+time_tolerance (de-sync as it can have the same effect as a cmd), before=now+output_grp.upr+time_tolerance.
199  auto offset = 2 * Utils::GetTimeTolerance();
200  if (input_fact->is_anti_fact())
201  copy = new AntiFact(input_fact->get_reference(0), now + offset, now + ref_group->get_upr()*Utils::GetBasePeriod(), 1, 1);
202  else
203  copy = new Fact(input_fact->get_reference(0), now + offset, now + ref_group->get_upr()*Utils::GetBasePeriod(), 1, 1);
204  for (uint16 i = 0; i < output_groups_.size(); ++i) {
205 
206  Group *output_group = output_groups_[i];
207  View *view = new View(input, true);
208  view->references_[0] = output_group;
209  view->references_[1] = input->references_[0];
210  view->code(VIEW_SYNC) = Atom::Float(View::SYNC_ONCE);
211  view->code(VIEW_RES) = Atom::Float(Utils::GetResilience(view->code(VIEW_RES).asFloat(), origin->get_upr(), output_group->get_upr()));
212  view->object_ = copy;
213  _Mem::Get()->inject(view);
214  if (i == 0) {
215 
216  primary_view = view;
217  if (ctpx_on_)
218  _Mem::Get()->inject_null_program(new HASTController(this, view, input_fact), output_group, output_group->get_upr()*Utils::GetBasePeriod(), true);
219  }
220  }
221  break;
222  }case View::SYNC_AXIOM: // inject a copy, sync_once, res=1, fact.before=next output_grp upr.
223  if (input_fact->is_anti_fact())
224  copy = new AntiFact(input_fact->get_reference(0), ref_group->get_prev_upr_time(now), ref_group->get_next_upr_time(now), 1, 1);
225  else
226  copy = new Fact(input_fact->get_reference(0), ref_group->get_prev_upr_time(now), ref_group->get_next_upr_time(now), 1, 1);
227  for (uint16 i = 0; i < output_groups_.size(); ++i) {
228 
229  Group *output_group = output_groups_[i];
230  View *view = new View(input, true);
231  view->references_[0] = output_group;
232  view->references_[1] = input->references_[0];
233  view->code(VIEW_SYNC) = Atom::Float(View::SYNC_ONCE_AXIOM);
234  view->code(VIEW_RES) = Atom::Float(1);
235  view->object_ = copy;
236  _Mem::Get()->inject(view);
237  if (i == 0)
238  primary_view = view;
239  }
240  break;
241  }
242 
243  if (trace_injections_) {
244 
245  const char* syncString = "";
246  switch (input->get_sync()) {
247  case View::SYNC_HOLD: syncString = "(HOLD)"; break;
248  case View::SYNC_ONCE: syncString = "(ONCE)"; break;
249  case View::SYNC_PERIODIC: syncString = "(PERIODIC)"; break;
250  case View::SYNC_AXIOM: syncString = "(AXIOM)"; break;
251  }
252  OUTPUT_LINE(AUTO_FOCUS, Utils::RelativeTime(Now()) << " A/F -> " << input->object_->get_oid() << "|" <<
253  primary_view->object_->get_oid() << " " << syncString);
254  }
255 
256  return primary_view;
257 }
258 
259 inline void AutoFocusController::notify(_Fact *target, View *input, TPXMap &map) {
260 
261  TPXMap::const_iterator m = map.find(target);
262  if (m != map.end()) { // shall always be the case.
263 
264  m->second->signal(input); // will spawn a ReductionJob holding a P<> on m->second.
265  map.erase(m);
266  }
267 }
268 
269 inline void AutoFocusController::dispatch_pred_success(Success* success, TPXMap &map) {
270 
271  TPXMap::const_iterator m;
272  for (m = map.begin(); m != map.end(); ++m)
273  m->second->ack_pred_success(success);
274 }
275 
276 inline void AutoFocusController::dispatch(View *input, _Fact *abstract_input, BindingMap *bm, bool &injected, TPXMap &map) {
277 
278  TPXMap::const_iterator m;
279  for (m = map.begin(); m != map.end(); ++m) {
280 
281  if (m->second->take_input(input, abstract_input, bm)) {
282 
283  if (!injected) {
284 
285  injected = true;
286  inject_input(input, abstract_input, bm);
287  }
288  }
289  }
290 }
291 
292 inline void AutoFocusController::dispatch_no_inject(View *input, _Fact *abstract_input, BindingMap *bm, TPXMap &map) {
293 
294  TPXMap::const_iterator m;
295  for (m = map.begin(); m != map.end(); ++m)
296  m->second->take_input(input, abstract_input, bm);
297 }
298 
299 void AutoFocusController::take_input(r_exec::View *input) {
300 
301  if (is_invalidated())
302  return;
303  if (input->object_->code(0).asOpcode() == Opcodes::Fact ||
304  input->object_->code(0).asOpcode() == Opcodes::AntiFact ||
305  input->object_->code(0).asOpcode() == Opcodes::MkRdx) // discard everything but facts, |facts and mk.rdx.
306  Controller::__take_input<AutoFocusController>(input);
307 }
308 
309 void AutoFocusController::reduce(r_exec::View *input) {
310 
311  Code *input_object = input->object_;
312  uint16 opcode = input_object->code(0).asOpcode();
313 
314  reductionCS_.enter();
315 
316  if (opcode == Opcodes::MkRdx) {
317 
318  Fact *f_ihlp = (Fact *)input_object->get_reference(MK_RDX_IHLP_REF);
319  BindingMap *bm = ((MkRdx *)input_object)->bindings_;
320  if (f_ihlp->get_reference(0)->code(0).asOpcode() == Opcodes::IMdl) { // handle new goals/predictions as new targets.
321 
322  Code *mdl = f_ihlp->get_reference(0)->get_reference(0);
323  Code *unpacked_mdl = mdl->get_reference(mdl->references_size() - MDL_HIDDEN_REFS);
324  uint16 obj_set_index = unpacked_mdl->code(MDL_OBJS).asIndex();
325 
326  // Get the first production in the set of productions.
327  uint16 production_set_index = input_object->code(MK_RDX_PRODS).asIndex();
328  uint16 production_reference_index = input_object->code(production_set_index + 1).asIndex();
329  Code *production = input_object->get_reference(production_reference_index);
330 
331  _Fact *pattern;
332  TPX *tpx;
333  Goal *goal = ((_Fact *)production)->get_goal();
334  if (goal != NULL) { // build a tpx to find models like M:[A -> B] where B is the goal target.
335 
336  pattern = (_Fact *)unpacked_mdl->get_reference(unpacked_mdl->code(obj_set_index + 1).asIndex()); // lhs.
337  tpx = build_tpx<GTPX>((_Fact *)production, pattern, bm, goal_ratings_, f_ihlp, f_ihlp->get_reference(0)->code(I_HLP_WEAK_REQUIREMENT_ENABLED).asBoolean());
338  goals_.insert(std::make_pair((_Fact *)production, tpx));
339  } else {
340 
341  Pred *pred = ((_Fact *)production)->get_pred();
342  if (pred != NULL) { // build a tpx to find models like M:[A -> |imdl M0] where M0 is the model that produced the prediction.
343 
344  pattern = (_Fact *)unpacked_mdl->get_reference(unpacked_mdl->code(obj_set_index + 2).asIndex()); // rhs.
345  tpx = build_tpx<PTPX>((_Fact *)production, pattern, bm, prediction_ratings_, f_ihlp, f_ihlp->get_reference(0)->code(I_HLP_WEAK_REQUIREMENT_ENABLED).asBoolean());
346  predictions_.insert(std::make_pair((_Fact *)production, tpx));
347  }
348  }
349  }
350  } else {
351 
352  bool success = (opcode == Opcodes::Fact);
353  if (success || opcode == Opcodes::AntiFact) { // discard everything but facts.
354 
355  Code *payload = input_object->get_reference(0);
356  uint16 opcode = payload->code(0).asOpcode();
357  if (opcode == Opcodes::Success) { // input_object is f->success->payload, where payload is f->g or f->p; trim down the target list, rate targets, signal tpx.
358 
359  _Fact *target = (_Fact *)payload->get_reference(0);
360  Goal *goal = target->get_goal();
361  if (goal != NULL) {
362 
363  notify(target, input, goals_);
364  } else { // prediction.
365 
366  notify(target, input, predictions_);
367  if (success) // a mdl has correctly predicted a GTPX's target: the GTPX shall not produce anything: we need to pass the prediction to all GTPX.
368  dispatch_pred_success((Success*)payload, goals_);
369  }
370  } else if (opcode == Opcodes::Perf)
371  inject_input(input, 2); // inject in all output groups but the primary and secondary.
372  else { // filter according to targets: inject (once) when possible and pass to TPX if any.
373 
374  if (pass_through_) {
375 
376  View* primary_view = NULL;
377  if (opcode != Opcodes::ICst) // don't inject again (since it comes from inside).
378  primary_view = inject_input(input);
379 
380  if (ptpx_on_) {
381  // The PTPX may need the input. Save it in cross_buffer_ up to tpx_time_horizon.
382  if (opcode == Opcodes::ICst) {
383  P<BindingMap> bm = ((ICST *)payload)->bindings_;
384  _Fact *abstract_f_ihlp = bm->abstract_f_ihlp((_Fact *)input_object);
385  cross_buffer_.push_back(Input(input, abstract_f_ihlp, bm));
386  }
387  else {
388  P<BindingMap> bm = new BindingMap();
389  P<_Fact> abstract_input = (_Fact *)bm->abstract_object(input_object, false);
390  cross_buffer_.push_back(Input(primary_view, abstract_input, bm));
391  }
392  }
393  } else {
394 
395  P<BindingMap> bm = new BindingMap();
396  if (opcode == Opcodes::ICst) { // dispatch but don't inject again (since it comes from inside).
397 
398  bm = ((ICST *)payload)->bindings_;
399  _Fact *abstract_f_ihlp = bm->abstract_f_ihlp((_Fact *)input_object);
400  dispatch_no_inject(input, abstract_f_ihlp, bm, goals_);
401  dispatch_no_inject(input, abstract_f_ihlp, bm, predictions_);
402  cross_buffer_.push_back(Input(input, abstract_f_ihlp, bm));
403  } else {
404 
405  P<_Fact> abstract_input = (_Fact *)bm->abstract_object(input_object, false);
406  bool injected = false;
407  dispatch(input, abstract_input, bm, injected, goals_);
408  dispatch(input, abstract_input, bm, injected, predictions_);
409  }
410  }
411  }
412  }
413  }
414 
415  reductionCS_.leave();
416 }
417 
418 void AutoFocusController::inject_hlps(const vector<P<Code> > &hlps) const { // inject in the primary group; models will be injected in the secondary group automatically.
419 
420  vector<View *> views;
421 
422  auto now = Now();
423 
424  vector<P<Code> >::const_iterator hlp;
425  for (hlp = hlps.begin(); hlp != hlps.end(); ++hlp) {
426 
427  View *view = new View(View::SYNC_ONCE, now, 0, -1, output_groups_[0], NULL, *hlp, 1); // SYNC_ONCE,sln=0,res=forever,act=1.
428  view->references_[0] = output_groups_[0];
429  views.push_back(view);
430  }
431 
432  _Mem::Get()->inject_hlps(views, output_groups_[0]);
433 }
434 
435 void AutoFocusController::copy_cross_buffer(r_code::list<Input> &destination) { // copy inputs so they can be flagged independently by the tpxs that share the cross buffer.
436 
437  reductionCS_.enter();
439  for (i = cross_buffer_.begin(Now()); i != cross_buffer_.end(); ++i)
440  destination.push_back(Input(*i));
441  reductionCS_.leave();
442 }
443 }
r_code::_View
Definition: r_code/object.h:164
core::P
Definition: base.h:103
r_code::Code
Definition: r_code/object.h:224
r_exec::View
Definition: view.h:102
r_code::time_buffer::iterator
Definition: time_buffer.h:108
r_code::list
Definition: list.h:99