Empirical
Processor.h
Go to the documentation of this file.
1 
19 #ifndef EMP_PROCESSOR_H
20 #define EMP_PROCESSOR_H
21 
22 #include <fstream>
23 #include <iostream>
24 #include <map>
25 
26 #include "../base/array.h"
27 #include "../base/Ptr.h"
28 #include "../base/vector.h"
29 #include "../tools/map_utils.h"
30 #include "../tools/Random.h"
31 #include "../tools/string_utils.h"
32 
33 #include "InstLib.h"
34 
35 namespace emp {
36 
37  template <typename HARDWARE>
38  class Processor {
39  public:
41 
42  HARDWARE hw;
43 
44  // This function is run every time scope changed (if, while, scope instructions, etc.)
45  // If we are moving to an outer scope (lower value) we need to close the scope we are in,
46  // potentially continuing with a loop.
47  bool UpdateScope(size_t new_scope, ScopeType type=ScopeType::BASIC) {
48  const size_t cur_scope = CurScope();
49  new_scope++; // Scopes are stored as one higher than regs (Outer is 0)
50  // Test if we are entering a deeper scope.
51  if (new_scope > cur_scope) {
52  scope_stack.emplace_back(new_scope, type, inst_ptr);
53  return true;
54  }
55 
56  // Otherwise we are potentially exiting the current scope. Loop back instead?
57  if (CurScopeType() == ScopeType::LOOP) {
58  inst_ptr = scope_stack.back().start_pos; // Move back to the beginning of the loop.
59  ExitScope(); // Clear former scope
60  ProcessInst( genome.sequence[inst_ptr] ); // Process loops start again.
61  return false; // We did NOT enter the new scope.
62  }
63 
64  // Or are we exiting a function?
66  // @CAO Make sure we exit multiple scopes if needed to close the function...
67  inst_ptr = call_stack.back(); // Return from the function call.
68  if (inst_ptr >= genome.sequence.size()) { // Test if call occured at end of genome.
69  ResetIP(); // ...and reset to the begnning if so.
70  } else {
71  call_stack.pop_back(); // Clear the return position from the call stack.
72  ExitScope(); // Leave the function scope.
73  }
74  ProcessInst( genome.sequence[inst_ptr] ); // Process the new instruction instead.
75  return false; // We did NOT enter the new scope.
76  }
77 
78  // If we made it here, we must simply exit the current scope and test again.
79  ExitScope();
80 
81  return UpdateScope(new_scope, type);
82  }
83 
84  // This function fast-forwards to the end of the specified scope.
85  // NOTE: Bypass scope always drops out of the innermost scope no matter the arg provided.
86  void BypassScope(size_t scope) {
87  scope++; // Scopes are stored as one higher than regs (Outer is 0)
88  if (CurScope() < scope) return; // Only continue if break is relevant for current scope.
89 
90  ExitScope();
91  while (inst_ptr+1 < genome.sequence.size()) {
92  inst_ptr++;
93  const size_t test_scope = InstScope(genome.sequence[inst_ptr]);
94 
95  // If this instruction sets the scope AND it's outside the one we want to end, stop here!
96  if (test_scope && test_scope <= scope) {
97  inst_ptr--;
98  break;
99  }
100  }
101  }
102 
103  public:
105  Processor<HARDWARE>(const genome_t & in_genome)
106  : genome(in_genome), regs(), inputs(), outputs(), stacks(), fun_starts()
107  , inst_ptr(0), scope_stack(), reg_stack(), call_stack(), errors(0), traits()
108  {
109  scope_stack.emplace_back(0, ScopeType::ROOT, 0); // Initial scope.
110  for (size_t i = 0; i < CPU_SIZE; i++) {
111  regs[i] = (double) i;
112  fun_starts[i] = -1;
113  }
114  }
115 
118 
121  Processor<HARDWARE>(const inst_lib_t & inst_lib) : Processor<HARDWARE>(Genome(&inst_lib)) { ; }
122 
124  Processor<HARDWARE>(const Processor<HARDWARE> &) = default;
125 
128 
130  virtual ~Processor<HARDWARE>() { ; }
131 
132  bool operator<(const this_t & other) const {
133  return genome < other.genome;
134  }
135 
137  void Reset() {
138  genome.sequence.resize(0); // Clear out genome
139  traits.resize(0); // Clear out traits
140  ResetHardware(); // Reset the full hardware
141  }
142 
144  virtual void ResetHardware() {
145  // Initialize registers to their posision. So Reg0 = 0 and Reg11 = 11.
146  for (size_t i = 0; i < CPU_SIZE; i++) {
147  regs[i] = (double) i;
148  inputs.clear();
149  outputs.clear();
150  stacks[i].resize(0);
151  fun_starts[i] = -1;
152  }
153  inst_ptr = 0; // Move IP back to beginning
154  scope_stack.resize(1); // Reset to outermost scope.
155  reg_stack.resize(0); // Clear saved registers.
156  call_stack.resize(0); // Clear call history.
157  errors = 0; // Clear all errors.
158  }
159 
161  void ResetIP() {
162  inst_ptr = 0;
163  while (scope_stack.size() > 1) ExitScope(); // Forcibly exit all scopes except root.
164  // Restore all remaining backed-up registers (likely backed up in outer-most scope).
165  while (reg_stack.size()) {
166  regs[reg_stack.back().reg_id] = reg_stack.back().value;
167  reg_stack.pop_back();
168  }
169  call_stack.resize(0);
170  }
171 
172  // Accessors
173  Ptr<const inst_lib_t> GetInstLib() const { return genome.inst_lib; }
174  inst_t GetInst(size_t pos) const { return genome.sequence[pos]; }
175  const genome_t & GetGenome() const { return genome; }
176  const size_t GetSize() const { return genome.sequence.size(); }
177  double GetReg(size_t id) const { return regs[id]; }
178  double GetInput(int id) const { return Find(inputs, id, 0.0); }
179  const std::unordered_map<int,double> & GetInputs() const { return inputs; }
180  size_t GetNumInputs() const { return inputs.size(); }
181  double GetOutput(int id) const { return Find(outputs, id, 0.0); }
182  const std::unordered_map<int,double> & GetOutputs() const { return outputs; }
183  size_t GetNumOutputs() const { return outputs.size(); }
184  const stack_t & GetStack(size_t id) const { return stacks[id]; }
185  int GetFunStart(size_t id) const { return fun_starts[id]; }
186  size_t GetIP() const { return inst_ptr; }
187  emp::vector<ScopeInfo> GetScopeStack() const { return scope_stack; }
188  size_t CurScope() const { return scope_stack.back().scope; }
189  ScopeType CurScopeType() const { return scope_stack.back().type; }
190  ScopeType GetScopeType(size_t id) { return genome.inst_lib->GetScopeType(id); }
191  emp::vector<RegBackup> GetRegStack() const { return reg_stack; }
192  emp::vector<size_t> GetCallStack() const { return call_stack; }
193  size_t GetNumErrors() const { return errors; }
194  double GetTrait(size_t id) const { return traits[id]; }
195  const emp::vector<double> & GetTraits() { return traits; }
196  size_t GetNumTraits() const { return traits.size(); }
197 
198  void SetInst(size_t pos, const inst_t & inst) { genome.sequence[pos] = inst; }
199  void SetInst(size_t pos, size_t id, size_t a0=0, size_t a1=0, size_t a2=0) {
200  genome.sequence[pos].Set(id, a0, a1, a2);
201  }
202  void SetGenome(const genome_t & g) { genome = g; }
203  void SetReg(size_t id, double val) { regs[id] = val; }
204  void SetInput(int input_id, double value) { inputs[input_id] = value; }
205  void SetInputs(const std::unordered_map<int,double> & vals) { inputs = vals; }
206  void SetInputs(std::unordered_map<int,double> && vals) { inputs = std::move(vals); }
207  void SetOutput(int output_id, double value) { outputs[output_id] = value; }
208  void SetOutputs(const std::unordered_map<int,double> & vals) { outputs = vals; }
209  void SetOutputs(std::unordered_map<int,double> && vals) { outputs = std::move(vals); }
210  double PopStack(size_t id) {
211  if (stacks[id].size() == 0) return 0.0;
212  double out = stacks[id].back();
213  stacks[id].pop_back();
214  return out;
215  }
216  void PushStack(size_t id, double value) {
217  if (stacks[id].size() >= STACK_CAP) return;
218  stacks[id].push_back(value);
219  }
220  void SetFunStart(size_t id, int value) { fun_starts[id] = value; }
221  void SetIP(size_t pos) { inst_ptr = pos; }
222  void PushRegInfo(size_t scope_id, size_t reg_id) {
223  reg_stack.emplace_back(scope_id, reg_id, regs[reg_id]);
224  }
225  void PushCallInfo(size_t pos) { call_stack.push_back(pos); }
226  void IncErrors() { errors++; }
227  void SetTrait(size_t id, double val) {
228  if (id >= traits.size()) traits.resize(id+1, 0.0);
229  traits[id] = val;
230  }
231  void PushTrait(double val) { traits.push_back(val); }
232 
233  inst_t GetRandomInst(Random & rand) {
234  return inst_t(rand.GetUInt(genome.inst_lib->GetSize()),
235  rand.GetUInt(CPU_SIZE), rand.GetUInt(CPU_SIZE), rand.GetUInt(CPU_SIZE));
236  }
237 
238  void RandomizeInst(size_t pos, Random & rand) { SetInst(pos, GetRandomInst(rand) ); }
239 
240  void PushInst(size_t id, size_t a0=0, size_t a1=0, size_t a2=0) {
241  genome.sequence.emplace_back(id, a0, a1, a2);
242  }
243  void PushInst(const std::string & name, size_t a0=0, size_t a1=0, size_t a2=0) {
244  size_t id = genome.inst_lib->GetID(name);
245  genome.sequence.emplace_back(id, a0, a1, a2);
246  }
247  void PushInst(const Instruction & inst) { genome.sequence.emplace_back(inst); }
248  void PushInst(Instruction && inst) { genome.sequence.emplace_back(inst); }
249  void PushRandom(Random & rand, const size_t count=1) {
250  for (size_t i = 0; i < count; i++) {
251  PushInst(GetRandomInst(rand));
252  }
253  }
254 
255  // Loading whole genomes.
256  bool Load(std::istream & input);
257 
259  void ProcessInst(const inst_t & inst) { genome.inst_lib->ProcessInst(*this, inst); }
260 
262  size_t InstScope(const inst_t & inst) const;
263 
265  void SingleProcess() {
266  emp_assert(genome.sequence.size() > 0); // A genome must exist to be processed.
267  if (inst_ptr >= genome.sequence.size()) ResetIP();
268  genome.inst_lib->ProcessInst(*this, genome.sequence[inst_ptr]);
269  inst_ptr++;
270  }
271 
273  void Process(size_t num_inst) { for (size_t i = 0; i < num_inst; i++) SingleProcess(); }
274 
276  void PrintInst(const inst_t & inst, std::ostream & os=std::cout) const;
277 
279  void PrintGenome(std::ostream & os=std::cout) const;
280  void PrintGenome(const std::string & filename) const;
281 
283  size_t PredictNextInst() const;
284 
286  void PrintState(std::ostream & os=std::cout) const;
287 
289  void Trace(size_t num_inst, std::ostream & os=std::cout) {
290  for (size_t i = 0; i < num_inst; i++) { PrintState(os); SingleProcess(); }
291  }
292  void Trace(size_t num_inst, const std::string & filename) {
293  std::ofstream of(filename);
294  Trace(num_inst, of);
295  of.close();
296  }
297 
298 
300  static void Inst_Inc(this_t & hw, const inst_t & inst) { ++hw.regs[inst.args[0]]; }
301  static void Inst_Dec(this_t & hw, const inst_t & inst) { --hw.regs[inst.args[0]]; }
302  static void Inst_Not(this_t & hw, const inst_t & inst) { hw.regs[inst.args[0]] = (hw.regs[inst.args[0]] == 0.0); }
303  static void Inst_SetReg(this_t & hw, const inst_t & inst) { hw.regs[inst.args[0]] = (double) inst.args[1]; }
304  static void Inst_Add(this_t & hw, const inst_t & inst) { hw.regs[inst.args[2]] = hw.regs[inst.args[0]] + hw.regs[inst.args[1]]; }
305  static void Inst_Sub(this_t & hw, const inst_t & inst) { hw.regs[inst.args[2]] = hw.regs[inst.args[0]] - hw.regs[inst.args[1]]; }
306  static void Inst_Mult(this_t & hw, const inst_t & inst) { hw.regs[inst.args[2]] = hw.regs[inst.args[0]] * hw.regs[inst.args[1]]; }
307 
308  static void Inst_Div(this_t & hw, const inst_t & inst) {
309  const double denom = hw.regs[inst.args[1]];
310  if (denom == 0.0) ++hw.errors;
311  else hw.regs[inst.args[2]] = hw.regs[inst.args[0]] / denom;
312  }
313 
314  static void Inst_Mod(this_t & hw, const inst_t & inst) {
315  const double base = hw.regs[inst.args[1]];
316  if (base == 0.0) ++hw.errors;
317  else hw.regs[inst.args[2]] = hw.regs[inst.args[0]] / base;
318  }
319 
320  static void Inst_TestEqu(this_t & hw, const inst_t & inst) { hw.regs[inst.args[2]] = (hw.regs[inst.args[0]] == hw.regs[inst.args[1]]); }
321  static void Inst_TestNEqu(this_t & hw, const inst_t & inst) { hw.regs[inst.args[2]] = (hw.regs[inst.args[0]] != hw.regs[inst.args[1]]); }
322  static void Inst_TestLess(this_t & hw, const inst_t & inst) { hw.regs[inst.args[2]] = (hw.regs[inst.args[0]] < hw.regs[inst.args[1]]); }
323 
324  static void Inst_If(this_t & hw, const inst_t & inst) { // args[0] = test, args[1] = scope
325  if (hw.UpdateScope(inst.args[1]) == false) return; // If previous scope is unfinished, stop!
326  if (hw.regs[inst.args[0]] == 0.0) hw.BypassScope(inst.args[1]); // If test fails, move to scope end.
327  }
328 
329  static void Inst_While(this_t & hw, const inst_t & inst) {
330  // UpdateScope returns false if previous scope isn't finished (e.g., while needs to loop)
331  if (hw.UpdateScope(inst.args[1], ScopeType::LOOP) == false) return;
332  if (hw.regs[inst.args[0]] == 0.0) hw.BypassScope(inst.args[1]); // If test fails, move to scope end.
333  }
334 
335  static void Inst_Countdown(this_t & hw, const inst_t & inst) { // Same as while, but auto-decriments test each loop.
336  // UpdateScope returns false if previous scope isn't finished (e.g., while needs to loop)
337  if (hw.UpdateScope(inst.args[1], ScopeType::LOOP) == false) return;
338  if (hw.regs[inst.args[0]] == 0.0) hw.BypassScope(inst.args[1]); // If test fails, move to scope end.
339  else hw.regs[inst.args[0]]--;
340  }
341 
342  static void Inst_Break(this_t & hw, const inst_t & inst) { hw.BypassScope(inst.args[0]); }
343  static void Inst_Scope(this_t & hw, const inst_t & inst) { hw.UpdateScope(inst.args[0]); }
344 
345  static void Inst_Define(this_t & hw, const inst_t & inst) {
346  if (hw.UpdateScope(inst.args[1]) == false) return; // Update which scope we are in.
347  hw.fun_starts[inst.args[0]] = (int) hw.inst_ptr; // Record where function should be exectuted.
348  hw.BypassScope(inst.args[1]); // Skip over the function definition for now.
349  }
350 
351  static void Inst_Call(this_t & hw, const inst_t & inst) {
352  // Make sure function exists and is still in place.
353  size_t def_pos = (size_t) hw.fun_starts[inst.args[0]];
354  if (def_pos >= hw.genome.sequence.size()
355  || hw.GetScopeType(hw.genome.sequence[def_pos].id) != ScopeType::FUNCTION) return;
356 
357  // Go back into the function's original scope (call is in that scope)
358  size_t fun_scope = hw.genome.sequence[def_pos].args[1];
359  if (hw.UpdateScope(fun_scope, ScopeType::FUNCTION) == false) return;
360  hw.call_stack.push_back(hw.inst_ptr+1); // Back up the call position
361  hw.inst_ptr = def_pos+1; // Jump to the function body (will adavance)
362  }
363 
364  static void Inst_Push(this_t & hw, const inst_t & inst) { hw.PushStack(inst.args[1], hw.regs[inst.args[0]]); }
365  static void Inst_Pop(this_t & hw, const inst_t & inst) { hw.regs[inst.args[1]] = hw.PopStack(inst.args[0]); }
366 
367  static void Inst_Input(this_t & hw, const inst_t & inst) {
368  // Determine the input ID and grab it if it exists; if not, return 0.0
369  int input_id = (int) hw.regs[ inst.args[0] ];
370  hw.regs[inst.args[1]] = Find(hw.inputs, input_id, 0.0);
371  }
372 
373  static void Inst_Output(this_t & hw, const inst_t & inst) {
374  // Save the date in the target reg to the specified output position.
375  int output_id = (int) hw.regs[ inst.args[1] ]; // Grab ID from register.
376  hw.outputs[output_id] = hw.regs[inst.args[0]]; // Copy target reg to appropriate output.
377  }
378 
379  static void Inst_CopyVal(this_t & hw, const inst_t & inst) { hw.regs[inst.args[1]] = hw.regs[inst.args[0]]; }
380 
381  static void Inst_ScopeReg(this_t & hw, const inst_t & inst) {
382  hw.reg_stack.emplace_back(hw.CurScope(), inst.args[0], hw.regs[inst.args[0]]);
383  }
384 
385  static const inst_lib_t & DefaultInstLib();
386  };
387 
388  template <typename HARDWARE>
389  size_t Processor<HARDWARE>::InstScope(const inst_t & inst) const {
390  if (genome.inst_lib->GetScopeType(inst.id) == ScopeType::NONE) return 0;
391  return inst.args[ genome.inst_lib->GetScopeArg(inst.id) ] + 1;
392  }
393 
394  template <typename HARDWARE>
395  void Processor<HARDWARE>::PrintInst(const inst_t & inst, std::ostream & os) const {
396  os << genome.inst_lib->GetName(inst.id);
397  const size_t num_args = genome.inst_lib->GetNumArgs(inst.id);
398  for (size_t i = 0; i < num_args; i++) {
399  os << ' ' << inst.args[i];
400  }
401  }
402 
403  template <typename HARDWARE>
404  void Processor<HARDWARE>::PrintGenome(std::ostream & os) const {
405  size_t cur_scope = 0;
406 
407  for (const inst_t & inst : genome.sequence) {
408  size_t new_scope = InstScope(inst);
409 
410  if (new_scope) {
411  if (new_scope == cur_scope) {
412  for (size_t i = 0; i < cur_scope; i++) os << ' ';
413  os << "----\n";
414  }
415  if (new_scope < cur_scope) {
416  cur_scope = new_scope-1;
417  }
418  }
419 
420  for (size_t i = 0; i < cur_scope; i++) os << ' ';
421  PrintInst(inst, os);
422  if (new_scope) {
423  if (new_scope > cur_scope) os << " --> ";
424  cur_scope = new_scope;
425  }
426  os << '\n';
427  }
428  }
429 
430  template <typename HARDWARE>
431  void Processor<HARDWARE>::PrintGenome(const std::string & filename) const {
432  std::ofstream of(filename);
433  PrintGenome(of);
434  of.close();
435  }
436 
437  template <typename HARDWARE>
439  // Determine if we are changing scope.
440  size_t new_scope = CPU_SIZE+1; // Default to invalid scope.
441  if (inst_ptr >= genome.sequence.size()) new_scope = 0;
442  else {
443  size_t inst_scope = InstScope(genome.sequence[inst_ptr]);
444  if (inst_scope) new_scope = inst_scope;
445  }
446 
447  // If we are not changing scope OR we are going to a deeper scope, execute next!
448  if (new_scope > CPU_SIZE || new_scope > CurScope()) return inst_ptr;
449 
450  // If we are at the end of a loop, assume we will jump back to the beginning.
451  if (CurScopeType() == ScopeType::LOOP) {
452  return scope_stack.back().start_pos;
453  }
454 
455  // If we are at the end of a function, assume we will jump back to the call.
457  size_t next_pos = call_stack.back();
458  if (next_pos >= genome.sequence.size()) next_pos = 0;
459  return next_pos;
460  }
461 
462  // If we have run past the end of the genome, we will start over.
463  if (inst_ptr >= genome.sequence.size()) return 0;
464 
465  // Otherwise, we exit the scope normally.
466  return inst_ptr;
467  }
468 
469  template <typename HARDWARE>
470  void Processor<HARDWARE>::PrintState(std::ostream & os) const {
471  size_t next_inst = PredictNextInst();
472 
473  os << " REGS: ";
474  for (size_t i = 0; i < CPU_SIZE; i++) os << "[" << regs[i] << "] ";
475  os << "\n INPUTS: ";
476  // for (size_t i = 0; i < CPU_SIZE; i++) os << "[" << Find(inputs, (int)i, 0.0) << "] ";
477  for (auto & x : inputs) os << "[" << x.first << "," << x.second << "] ";
478  os << "\n OUTPUTS: ";
479  //for (size_t i = 0; i < CPU_SIZE; i++) os << "[" << Find(outputs, (int)i, 0.0) << "] ";
480  for (auto & x : outputs) os << "[" << x.first << "," << x.second << "] ";
481  os << std::endl;
482 
483  os << "IP:" << inst_ptr;
484  if (inst_ptr != next_inst) os << "(-> " << next_inst << ")";
485  os << " scope:" << CurScope()
486  << " (";
487  if (next_inst < genome.sequence.size()) { // For interpreter mode
488  PrintInst(genome.sequence[next_inst], os);
489  }
490  os << ")"
491  << " errors: " << errors
492  << std::endl;
493 
494  // @CAO Still need:
495  // emp::array< emp::vector<double>, CPU_SIZE > stacks;
496  // emp::array< int, CPU_SIZE> fun_starts;
497  // emp::vector<RegBackup> reg_stack;
498  // emp::vector<size_t> call_stack;
499  }
500 
502  template <typename HARDWARE>
504  static inst_lib_t inst_lib;
505 
506  if (inst_lib.GetSize() == 0) {
507  inst_lib.AddInst("Inc", Inst_Inc, 1, "Increment value in reg Arg1");
508  inst_lib.AddInst("Dec", Inst_Dec, 1, "Decrement value in reg Arg1");
509  inst_lib.AddInst("Not", Inst_Not, 1, "Logically toggle value in reg Arg1");
510  inst_lib.AddInst("SetReg", Inst_SetReg, 2, "Set reg Arg1 to numerical value Arg2");
511  inst_lib.AddInst("Add", Inst_Add, 3, "regs: Arg3 = Arg1 + Arg2");
512  inst_lib.AddInst("Sub", Inst_Sub, 3, "regs: Arg3 = Arg1 - Arg2");
513  inst_lib.AddInst("Mult", Inst_Mult, 3, "regs: Arg3 = Arg1 * Arg2");
514  inst_lib.AddInst("Div", Inst_Div, 3, "regs: Arg3 = Arg1 / Arg2");
515  inst_lib.AddInst("Mod", Inst_Mod, 3, "regs: Arg3 = Arg1 % Arg2");
516  inst_lib.AddInst("TestEqu", Inst_TestEqu, 3, "regs: Arg3 = (Arg1 == Arg2)");
517  inst_lib.AddInst("TestNEqu", Inst_TestNEqu, 3, "regs: Arg3 = (Arg1 != Arg2)");
518  inst_lib.AddInst("TestLess", Inst_TestLess, 3, "regs: Arg3 = (Arg1 < Arg2)");
519  inst_lib.AddInst("If", Inst_If, 2, "If reg Arg1 != 0, scope -> Arg2; else skip scope", ScopeType::BASIC, 1);
520  inst_lib.AddInst("While", Inst_While, 2, "Until reg Arg1 != 0, repeat scope Arg2; else skip", ScopeType::LOOP, 1);
521  inst_lib.AddInst("Countdown", Inst_Countdown, 2, "Countdown reg Arg1 to zero; scope to Arg2", ScopeType::LOOP, 1);
522  inst_lib.AddInst("Break", Inst_Break, 1, "Break out of scope Arg1");
523  inst_lib.AddInst("Scope", Inst_Scope, 1, "Enter scope Arg1", ScopeType::BASIC, 0);
524  inst_lib.AddInst("Define", Inst_Define, 2, "Build function Arg1 in scope Arg2", ScopeType::FUNCTION, 1);
525  inst_lib.AddInst("Call", Inst_Call, 1, "Call previously defined function Arg1");
526  inst_lib.AddInst("Push", Inst_Push, 2, "Push reg Arg1 onto stack Arg2");
527  inst_lib.AddInst("Pop", Inst_Pop, 2, "Pop stack Arg1 into reg Arg2");
528  inst_lib.AddInst("Input", Inst_Input, 2, "Pull next value from input Arg1 into reg Arg2");
529  inst_lib.AddInst("Output", Inst_Output, 2, "Push reg Arg1 into output Arg2");
530  inst_lib.AddInst("CopyVal", Inst_CopyVal, 2, "Copy reg Arg1 into reg Arg2");
531  inst_lib.AddInst("ScopeReg", Inst_ScopeReg, 1, "Backup reg Arg1; restore at end of scope");
532 
533  for (size_t i = 0; i < CPU_SIZE; i++) {
534  inst_lib.AddArg(to_string((int)i), i); // Args can be called by value
535  inst_lib.AddArg(to_string("Reg", 'A'+(char)i), i); // ...or as a register.
536  }
537  }
538 
539  return inst_lib;
540  }
541 }
542 
543 
544 #endif
ScopeType CurScopeType() const
Definition: Processor.h:189
double GetOutput(int id) const
Definition: Processor.h:181
static void Inst_CopyVal(this_t &hw, const inst_t &inst)
Definition: Processor.h:379
static void Inst_Define(this_t &hw, const inst_t &inst)
Definition: Processor.h:345
void SingleProcess()
Process the NEXT instruction pointed to be the instruction pointer.
Definition: Processor.h:265
void IncErrors()
Definition: Processor.h:226
This file maintains information about instructions availabel in virtual hardware. ...
static void Inst_TestLess(this_t &hw, const inst_t &inst)
Definition: Processor.h:322
std::string to_string(ALL_TYPES &&...all_values)
Definition: string_utils.h:511
static void Inst_Call(this_t &hw, const inst_t &inst)
Definition: Processor.h:351
ScopeType GetScopeType(size_t id)
Definition: Processor.h:190
static void Inst_Sub(this_t &hw, const inst_t &inst)
Definition: Processor.h:305
static const inst_lib_t & DefaultInstLib()
This static function can be used to access the generic AvidaGP instruction library.
Definition: Processor.h:503
emp::vector< ScopeInfo > GetScopeStack() const
Definition: Processor.h:187
static void Inst_Div(this_t &hw, const inst_t &inst)
Definition: Processor.h:308
bool UpdateScope(size_t new_scope, ScopeType type=ScopeType::BASIC)
Definition: Processor.h:47
static void Inst_Mult(this_t &hw, const inst_t &inst)
Definition: Processor.h:306
const genome_t & GetGenome() const
Definition: Processor.h:175
static void Inst_Push(this_t &hw, const inst_t &inst)
Definition: Processor.h:364
size_t PredictNextInst() const
Figure out which instruction is going to actually be run next SingleProcess()
Definition: Processor.h:438
void PushTrait(double val)
Definition: Processor.h:231
void SetInputs(const std::unordered_map< int, double > &vals)
Definition: Processor.h:205
static void Inst_ScopeReg(this_t &hw, const inst_t &inst)
Definition: Processor.h:381
A versatile and non-patterned pseudo-random-number generator (Mersenne Twister).
Definition: ce_random.h:52
double PopStack(size_t id)
Definition: Processor.h:210
static void Inst_Inc(this_t &hw, const inst_t &inst)
Instructions.
Definition: Processor.h:300
emp::vector< size_t > GetCallStack() const
Definition: Processor.h:192
size_t CurScope() const
Definition: Processor.h:188
HARDWARE hw
Definition: Processor.h:42
size_t GetNumTraits() const
Definition: Processor.h:196
void SetReg(size_t id, double val)
Definition: Processor.h:203
bool operator<(const this_t &other) const
Definition: Processor.h:132
void SetIP(size_t pos)
Definition: Processor.h:221
double GetTrait(size_t id) const
Definition: Processor.h:194
void PushInst(size_t id, size_t a0=0, size_t a1=0, size_t a2=0)
Definition: Processor.h:240
void Trace(size_t num_inst, std::ostream &os=std::cout)
Trace the instructions being exectured, with full CPU details.
Definition: Processor.h:289
static void Inst_If(this_t &hw, const inst_t &inst)
Definition: Processor.h:324
constexpr uint32_t GetUInt(const uint32_t max)
Definition: ce_random.h:191
void RandomizeInst(size_t pos, Random &rand)
Definition: Processor.h:238
void SetGenome(const genome_t &g)
Definition: Processor.h:202
Ptr< const inst_lib_t > GetInstLib() const
Definition: Processor.h:173
void SetInst(size_t pos, const inst_t &inst)
Definition: Processor.h:198
void PrintInst(const inst_t &inst, std::ostream &os=std::cout) const
Print out a single instruction, with its arguments.
Definition: Processor.h:395
void SetFunStart(size_t id, int value)
Definition: Processor.h:220
const size_t GetSize() const
Definition: Processor.h:176
auto Find(const MAP_T &in_map, const KEY_T &key, const typename MAP_T::mapped_type &dval)
Definition: map_utils.h:29
void BypassScope(size_t scope)
Definition: Processor.h:86
static const PrintStr endl("<br>")
Pre-define emp::endl to insert a "<br>" and thus acting like a newline.
static void Inst_Break(this_t &hw, const inst_t &inst)
Definition: Processor.h:342
size_t GetNumInputs() const
Definition: Processor.h:180
size_t InstScope(const inst_t &inst) const
Determine the scope associated with a particular instruction.
Definition: Processor.h:389
static void Inst_Output(this_t &hw, const inst_t &inst)
Definition: Processor.h:373
void PushInst(const std::string &name, size_t a0=0, size_t a1=0, size_t a2=0)
Definition: Processor.h:243
bool Load(std::istream &input)
void ResetIP()
Reset the instruction pointer to the beginning of the genome AND reset scope.
Definition: Processor.h:161
void PrintGenome(std::ostream &os=std::cout) const
Print out this program.
Definition: Processor.h:404
static void Inst_Countdown(this_t &hw, const inst_t &inst)
Definition: Processor.h:335
void PushRegInfo(size_t scope_id, size_t reg_id)
Definition: Processor.h:222
inst_t GetInst(size_t pos) const
Definition: Processor.h:174
void SetOutput(int output_id, double value)
Definition: Processor.h:207
void PushRandom(Random &rand, const size_t count=1)
Definition: Processor.h:249
const std::unordered_map< int, double > & GetInputs() const
Definition: Processor.h:179
const emp::vector< double > & GetTraits()
Definition: Processor.h:195
static void Inst_TestNEqu(this_t &hw, const inst_t &inst)
Definition: Processor.h:321
void SetInput(int input_id, double value)
Definition: Processor.h:204
void SetInst(size_t pos, size_t id, size_t a0=0, size_t a1=0, size_t a2=0)
Definition: Processor.h:199
static void Inst_Scope(this_t &hw, const inst_t &inst)
Definition: Processor.h:343
size_t GetNumErrors() const
Definition: Processor.h:193
static void Inst_TestEqu(this_t &hw, const inst_t &inst)
Definition: Processor.h:320
void Trace(size_t num_inst, const std::string &filename)
Definition: Processor.h:292
void SetInputs(std::unordered_map< int, double > &&vals)
Definition: Processor.h:206
If we are in emscripten, make sure to include the header.
Definition: array.h:37
void PrintState(std::ostream &os=std::cout) const
Print out the state of the virtual CPU.
Definition: Processor.h:470
int GetFunStart(size_t id) const
Definition: Processor.h:185
void SetTrait(size_t id, double val)
Definition: Processor.h:227
size_t GetIP() const
Definition: Processor.h:186
A single instruction in a linear genome.
Definition: LinearCode.h:20
Definition: Ptr.h:711
Build a debug wrapper emp::vector around std::vector.
Definition: vector.h:42
void SetOutputs(const std::unordered_map< int, double > &vals)
Definition: Processor.h:208
ScopeType
Definition: InstLib.h:27
double GetInput(int id) const
Definition: Processor.h:178
void SetOutputs(std::unordered_map< int, double > &&vals)
Definition: Processor.h:209
void Process(size_t num_inst)
Process the next SERIES of instructions, directed by the instruction pointer.
Definition: Processor.h:273
static void Inst_Dec(this_t &hw, const inst_t &inst)
Definition: Processor.h:301
void PushInst(Instruction &&inst)
Definition: Processor.h:248
#define emp_assert(...)
Definition: assert.h:199
size_t GetNumOutputs() const
Definition: Processor.h:183
InstLib maintains a set of instructions for use in virtual hardware.
Definition: InstLib.h:34
static void Inst_Input(this_t &hw, const inst_t &inst)
Definition: Processor.h:367
static void Inst_Add(this_t &hw, const inst_t &inst)
Definition: Processor.h:304
const stack_t & GetStack(size_t id) const
Definition: Processor.h:184
static void Inst_While(this_t &hw, const inst_t &inst)
Definition: Processor.h:329
void Reset()
Reset the entire CPU to a starting state, without a genome.
Definition: Processor.h:137
static void Inst_Pop(this_t &hw, const inst_t &inst)
Definition: Processor.h:365
void PushStack(size_t id, double value)
Definition: Processor.h:216
virtual void ResetHardware()
Reset just the CPU hardware, but keep the genome and traits.
Definition: Processor.h:144
void ProcessInst(const inst_t &inst)
Process a specified instruction, provided by the caller.
Definition: Processor.h:259
emp::vector< RegBackup > GetRegStack() const
Definition: Processor.h:191
inst_t GetRandomInst(Random &rand)
Definition: Processor.h:233
static void Inst_SetReg(this_t &hw, const inst_t &inst)
Definition: Processor.h:303
void PushInst(const Instruction &inst)
Definition: Processor.h:247
double GetReg(size_t id) const
Definition: Processor.h:177
const std::unordered_map< int, double > & GetOutputs() const
Definition: Processor.h:182
void PushCallInfo(size_t pos)
Definition: Processor.h:225
static void Inst_Mod(this_t &hw, const inst_t &inst)
Definition: Processor.h:314
static void Inst_Not(this_t &hw, const inst_t &inst)
Definition: Processor.h:302
Definition: Processor.h:38