5.2.3 Accessing Hard-Coded Objects in CSS

In general, hard-coded objects (and their members and member functions) are treated just as they would be in C or C++. Objects that have been created (i.e., Networks, Units, etc) can be referred to by their path names, which start with the root object (PDPRoot). While you could type root.projects[0], for example, to refer to the first project, it is easier to use the abbreviation of a preceding period to stand for root, resulting in: .projects[0].

The following examples were performed on the XOR example project in Bp++. In order to examine the project in CSS, one could simply use the print command on the path of this object:

bp++> print .projects[0]
.projects[0] Proj (refn=1) {
  ta_Base*        owner           = .projects;
  String          name            = Proj;
  WinBase*        win_owner       = root;
  WinGeometry     win_pos         = {lft=4: bot=74: wd=535: ht=24: };
  WinGeometry     root_win_pos    = {lft=9: bot=79: wd=161: ht=23: };
  TypeDefault_MGroup   defaults           =  Size: 5 (TypeDefault);
  BaseSpec_MGroup   specs         =  Size: 3 (BaseSpec);
  Network_MGroup   networks       =  Size: 1 (Network);
  Environment_MGroup   environments       =  Size: 1 (Environment);
  Process_MGroup   processes      =  Size: 5 (SchedProcess);
  PDPLog_MGroup   logs            =  Size: 2 (TextLog);
  Script_MGroup   scripts         =  Size: 1 (Script);
}

The first network within this project would then be referred to as .projects[0].networks[0]:

bp++> print .projects[0].networks[0]
.projects[0].networks[0] XOR (refn=15) {
  ta_Base*        owner           = .projects[0].networks;
  String          name            = XOR;
  WinBase*        win_owner       = .projects[0];
  WinGeometry     win_pos         = {lft=4: bot=3: wd=536: ht=390: };
  WinView_MGroup   views          =  Size: 1 (NetView);
  Layer_MGroup    layers          =  Size: 3 (Layer);
  Project*        proj            = .projects[0];
  TDGeometry      pos             = {x=0: y=0: z=0: };
  TDGeometry      max_size        = {x=2: y=2: z=3: };
  int             epoch           = 0;
  Network::Layer_Layout   lay_layout      = THREE_D;
}

You can also use a shortcut by just typing .networks[0], which finds the first member with the name networks in a search starting at the root object and scanning down the first branch of the tree of objects (i.e., looking in the first element of every group along the way).

Scoped types such as Network::Layer_Layout which appear in the above class are referred to just as they would be in C++:

bp++> .networks[0].lay_layout = Network::TWO_D;

As you can see, setting the values of hard-coded object variables simply amounts to typing in the appropriate C/C++ statement.

Type information (obtained via the TypeAccess system) about hard-coded objects can be obtained with the type command:

bp++> type Network
class Network : PDPWinMgr : WinMgr : WinBase : ta_NBase : ta_Base {
// The Network 

  // sub-types
  enum Layer_Layout {   // Visual mode of layer position/view
    TWO_D            = 0;       // all z = 0, no skew
    THREE_D          = 1;       // z = layer index, default skew
  }

  // members
  ta_Base*        owner;          //   pointer to owner
  String          name;           //   name of the object
  .
  .
  TDGeometry      max_size;       //   max size in each dim
  int             epoch;          //   epoch counter
  Network::Layer_Layout   lay_layout;     // Visual mode of layer 

  // functions
  void            UnSafeCopy(ta_Base* na);
  ta_Base*        GetOwner(TypeDef* tp);
  .
  .
  .
  void            InitWtState();        // Initialize the weights
  .
  .
  void            Compute_dWt();        // update weights for whole net
  void            Copy_Weights(const Network* src);  
  void            Enforce_Layout(Network::Layer_Layout layout_type);
}

This shows the inheritance of this object, any sub-types that are defined within it, and all of its members and functions (including those it inherits from other classes).

In addition, there is Tab-completion for path names and types in the CSS prompt-level script interface. Thus, as you are typing a path, if you hit the Tab key, it will try to complete the path. If there are multiple completions, hitting Tab twice will display them.

In order to call member functions of hard-coded classes, simply give the path to the object, followed by the member function, with any arguments that it might require (or none).

bp++> .networks[0].InitWtState();
bp++> 

It is possible to create pointers to hard-coded objects. Simply declare a pointer variable with the appropriate type, and assign it to the given object by referring to its path:

bp++> Unit* un;
bp++> un = .networks[0].layers[1].units[0];
bp++> print un
.projects[0].networks[0].layers[1].units[0] hid_1 (refn=6) {
  ta_Base*        owner           = .projects[0].networks[0].layers[1].units;
  String          name            = hid_1;
  UnitSpec_SPtr   spec            = {type=BpUnitSpec: spec=.specs[0]: };
  TDGeometry      pos             = {x=0: y=0: z=0: };
  Unit::ExtType   ext_flag        = NO_EXTERNAL;
  float           targ            = 0;
  float           ext             = 0;
  float           act             = 0;
  float           net             = 0;
  Con_Group       recv            =  Size: 0.1.2 (BpCon);
  Con_Group       send            =  Size: 0.1.1 (BpCon);
  BpCon           bias            = BpCon;
  float           err             = 0;
  float           dEdA            = 0;
  float           dEdNet          = 0;
}

There are two ways to create new hard-coded objects. The preferred way is to call one of the New functions on the group-like objects (List or Group, see section 8.2 Groups), which will create the object and add it to the group, so that it can be referred to by its path as just described.

bp++> .layers[1].units.List();

Elements of List:  (2)
hid_1   hid_2 
bp++> .layers[1].units.New(1);
bp++> .layers[1].units.List();

Elements of List:  (3)
hid_1   hid_2    
bp++> .layers[1].units[2].name = "new_guy";
bp++> .layers[1].units.List();

Elements of List:  (3)
hid_1   hid_2   new_guy 

Finally, it is possible to create new instances of hard-coded object types through the C++ new operator, which is especially useful in order to take advantage of some of the handy built-in types like arrays (see section 8.3 Arrays):

bp++> float_RArray* ar = new float_RArray;
bp++> print ar
[0];

bp++> ar.Add(.25);
bp++> ar.Add(.55);
bp++> ar.Add(.11);
bp++> print ar
[3] 0.25 0.55 0.11;

bp++> print ar.Mean();
(Real)  = 0.303333
bp++> print ar.Var();
(Real)  = 0.101067
bp++> ar.Sort();
bp++> print ar
[3] 0.11 0.25 0.55;

Remember to delete those objects which you have created in this fashion:

bp++> delete ar;
bp++> print ar
(float_RArray) ar = 0

(the ar = 0 means that it is a null pointer). Be sure not to use the delete operator on those objects which were created with the group's New function, which should be Removed from the group, not deleted directly (see section 8.2 Groups).