source: framspy/frams-test.py @ 1116

Last change on this file since 1116 was 1116, checked in by Maciej Komosinski, 3 years ago

Improved performance significantly by moving the step-by-step simulation loop from python to FramScript?

File size: 7.2 KB
RevLine 
[1089]1"""Simple examples of using the "frams" module to communicate directly with the Framsticks library (dll/so).
2
3For an introduction to Framsticks, its usage and scripting, see https://www.youtube.com/playlist?list=PLkPlXm7pOPatTl3_Gecx8ZaCVGeH4UV1L
4For a list of available classes, objects, methods and fields, see http://www.framsticks.com/files/classdoc/
[1103]5For a number of examples of scripting, see the "scripts" directory in the Framsticks distribution.
[1089]6"""
7
[1081]8import sys
[1103]9import json
[1078]10import frams
11
[1087]12frams.init(*(sys.argv[1:]))  # pass whatever args we have, init() is the right place to deal with different scenarios:
[1085]13# frams.init() - should try to figure out everything (and might fail)
[1087]14# frams.init('path/to/lib') - load the library from the specified directory and configure Framsticks path as "data" inside this directory
15# frams.init('path/to/lib','-d/tmp/workdir/data') - as above, but set the working (writable) directory somewhere else (see also -D)
16# frams.init('path/to/lib','-Lframs-objects-alt.dll') - use specified library location and non-default file name
[1078]17
18print('Available objects:', dir(frams))
19print()
20
21
22def extValueDetails(v):
[1089]23        """A helper function to display basic information about a variable of type ExtValue."""
24        return '\t"' + str(v) + '"    frams type=' + str(v._type()) + '    frams class=' + str(v._class()) + '    python type=' + str(type(v._value()))
[1078]25
26
[1089]27dic_as_string = '[100,2.2,"abc",[null,[],{}],XYZ[9,8,7]]'
28print("We have the following string:\n\t'%s'" % dic_as_string)
29print("Looks like a serialized dictionary, let's ask Framsticks String.deserialize() to do its job.")
30v = frams.String.deserialize(dic_as_string)
31print("Framsticks String.deserialize() returned\n\t", type(v))
32print("More specifically, it is:")
[1078]33print(extValueDetails(v))
[1089]34print("Even though it is ExtValue (Framsticks' Vector), it supports iteration like a python vector, so let's inspect its elements:")
[1078]35for e in v:
36        print(extValueDetails(e))
37
[1114]38print("\nNow let's play with the Framsticks simulator. Let's create a Genotype object and set fields in its custom 'data' dictionary.")
[1078]39g = frams.GenePools[0].add('X')
[1089]40g.name = "Snakis Py"
[1078]41g.data['custom'] = 123.456
[1089]42g.data['a'] = 'b'  # implicit conversion, looks like python dictionary but still converts '3' and '4' to ExtValue
43dic = frams.Dictionary.new()  # let's create a Dictionary object from Framsticks
44dic.set('1', '2')  # calling set() from Framsticks Dictionary
45dic['3'] = '4'  # implicit conversion, looks like python dictionary but still converts '3' and '4' to ExtValue
46g.data['d'] = dic
47print(extValueDetails(g))
[1078]48
[1089]49print("Let's add a few mutants and display their data:")
[1078]50for more in range(5):
51        frams.GenePools[0].add(frams.GenMan.mutate(g.geno))
52
53for g in frams.GenePools[0]:
[1089]54        print("\t%d. name='%s'\tgenotype='%s'\tdata=%s" % (g.index._value(), str(g.name), str(g.genotype), str(g.data)))
55
56print("Let's now change some property of the simulation. Current water level is", frams.World.wrldwat)
57frams.World.wrldwat = 0.5
58print("Now water level is", frams.World.wrldwat)
59frams.World.wrldwat = frams.World.wrldwat._value() + 0.7
60print("Now water level is", frams.World.wrldwat)
61
[1092]62initial_genotype = 'X(X,RX(X[T],X[G]))'  # simple body with touch and gyroscope sensors
[1089]63print("Let's perform a few simulation steps of the initial genotype:", initial_genotype)
64frams.ExpProperties.initialgen = initial_genotype
65frams.ExpProperties.p_mut = 0  # no mutation (the selection procedure will clone our initial genotype)
66frams.ExpProperties.p_xov = 0  # no crossover (the selection procedure will clone our initial genotype)
67frams.Populations[0].initial_nn_active = 1  # immediate simulation of neural network - no "waiting for stabilization" period
68frams.World.wrldg = 5  # gravity=5x default, let it fall quickly
69
70frams.Simulator.init()  # adds initial_genotype to gene pool (calls onInit() from standard.expdef)
71frams.Simulator.start()  # this does not actually start the simulation, just sets the "Simulator.running" status variable
72step = frams.Simulator.step  # cache reference to avoid repeated lookup in the loop (just for performance)
73for s in range(15):
74        step()  # first step performs selection and revives one genotype according to standard.expdef rules
75        creature = frams.Populations[0][0]  # FramScript Creature object
76        mechpart0 = creature.getMechPart(0)
77        print('Step# = %d' % frams.Simulator.stepNumber._value(),
78              '\tSimulated_creatures =', frams.Populations[0].size._value(),
79              "\tpart0_xyz = (% .2f,% .2f,% .2f)" % (mechpart0.x._value(), mechpart0.y._value(), mechpart0.z._value()),
80              "\ttouch = % .3f\tgyro = % .3f" % (creature.getNeuro(0).state._value(), creature.getNeuro(1).state._value()))
81frams.Simulator.stop()
82
[1103]83# changing expdef
84testgenotype = "XrrX[G][-1:80][|,-1:0.9]X[|,-2:-21]"
85evaluations = 100
86print("\nLet's change the experiment definition (expdef) and evaluate genotype '%s' %d times." % (testgenotype, evaluations))
87frams.Simulator.expdef = "standard-eval"
88frams.ExpProperties.evalcount = evaluations
89frams.ExpProperties.cr_v = 1
90frams.ExpProperties.evalplan = ":velocity,vertpos,fit_stdev,time"
91frams.GenePools[0].add(testgenotype)
92frams.ExpProperties.evalsavefile = ""  # no need to store results in a file - we will get evaluations directly from Genotype's "data" field
93frams.Simulator.init()
94frams.Simulator.start()
[1116]95# step = frams.Simulator.step  # cache reference to avoid repeated lookup in the loop (just for performance)
96# while frams.Simulator.running._int():  # standard-eval.expdef sets running to 0 when the evaluation is complete
97#       step()
98frams.Simulator.eval("while(Simulator.running) Simulator.step();")  # loop in FramScript much faster than loop in python
[1103]99for g in frams.GenePools[0]:  # loop over all genotypes, even though we know we added only one
100        serialized_dict = frams.String.serialize(g.data[frams.ExpProperties.evalsavedata._value()])
101        print(json.loads(serialized_dict._string()))
102
[1112]103# sampling a Model in 3D
104geno = "RXX(X,CXXX)"
105print("\nNow build a Model from the genotype '%s' and sample it in 3D, then print a 2D projection" % geno)
106import numpy as np
107
108matrix = np.zeros((20, 20, 20), dtype=int)  # 3D matrix, "voxels"
109m = frams.ModelGeometry.forModel(frams.Model.newFromString(geno));
110m.geom_density = 20;
111for p in m.voxels():
112        # print('%f %f %f ' % (p.x._value(), p.y._value(), p.z._value()))
113        matrix[int(p.x._value() * 5 + 2), int(p.y._value() * 5 + 5), int(p.z._value() * 5 + 6)] += 1  # scaling and offsets adjusted manually to fit the matrix nicely
114matrix = np.sum(matrix, axis=1)  # sum along axis, make 2D from 3D ("projection")
115np.set_printoptions(formatter={'int': lambda x: ('.' if x == 0 else str(x // 18))})  # print zeros as dots, x//18 to fit a larger range into a single digit
116print(matrix)
117np.set_printoptions()  # avoids straange python errors: frams.py, line 48, in __del__ AttributeError: 'NoneType' object has no attribute 'extFree'
118
119#
120#
[1089]121# Note that implementing a complete expdef, especially a complex one, entirely in python may be inconvenient or impractical
122# because you do not have access to "event handlers" like you have in FramScript - onStep(), onBorn(), onDied(), onCollision() etc.,
123# so you would have to check various conditions in python in each simulation step to achieve the same effect.
Note: See TracBrowser for help on using the repository browser.