The Core (xentica.core)¶
Xentica core functionality is available via modules from this package.
In addition, you may use core
package as a shortcut to the main
classes of the framework.
- Base classes
core.CellularAutomaton
→xentica.core.base.CellularAutomaton
core.Experiment
→xentica.core.experiment.Experiment
- Lattices
core.OrthogonalLattice
→xentica.core.topology.lattice.OrthogonalLattice
- Neighborhoods
core.MooreNeighborhood
→xentica.core.topology.neighborhood.MooreNeighborhood
core.VonNeumannNeighborhood
→xentica.core.topology.neighborhood.VonNeumannNeighborhood
- Borders
core.TorusBorder
→xentica.core.topology.border.TorusBorder
core.StaticBorder
→xentica.core.topology.border.StaticBorder
- Properties
core.IntegerProperty
→xentica.core.properties.IntegerProperty
core.FloatProperty
→xentica.core.properties.FloatProperty
core.TotalisticRuleProperty
→xentica.core.properties.TotalisticRuleProperty
core.RandomProperty
→xentica.core.properties.RandomProperty
- Parameters
core.Parameter
→xentica.core.parameters.Parameter
- Variables
core.IntegerVariable
→xentica.core.variables.IntegerVariable
core.FloatVariable
→xentica.core.variables.FloatVariable
The classes listed above are all you need to build CA models and experiments with Xentica, unless you are planning to implement custom core features like new lattices, borders, etc.
Base Classes (xentica.core.base)¶
The module with the base class for all CA models.
All Xentica models should be inherited from CellularAutomaton
base class. Inside the model, you should correctly define the
Topology
class and describe the CA logic in emit()
,
absorb()
and color()
methods.
Topology
is the place where you define the dimensionality,
lattice, neighborhood and border effects for your CA. See
xentica.core.topology
package for details.
The logic of the model will follow Buffered State Cellular Automaton (BSCA) principle. In general, every cell mirrors its state in buffers by the number of neighbors, each buffer intended for one of the neighbors. Then, at each step, the interaction between cells is performed via buffers in 2-phase emit/absorb process. A more detailed description of the BSCA principle is available in The Core section of The Concept document.
emit()
describes the logic of the first phase of BSCA. At this
phase, you should fill the cell’s buffers with corresponding values,
depending on the cell’s main state and (optionally) on neighbors’ main
states. The easiest logic is to just copy the main state to
buffers. It is especially useful when you’re intending to emulate
classic CA (like Conway’s Life) with BSCA. Write access to the main
state is prohibited there.
absorb()
describes the logic of the second phase of BSCA. At this
phase, you should set the cell’s main state, depending on neighbors’
buffered states. Write access to buffers is prohibited there.
color()
describes how to calculate cell’s color from its raw
state. See detailed instructions on it in
xentica.core.color_effects
.
The logic of the functions from above will be translated into C code
at the moment of class instance creation. For the further instructions
on how to use the cell’s main and buffered states, see
xentica.core.properties
, for the instructions on variables and
expressions with them, see xentica.core.variables
. Another
helpful thing is meta-parameters, which are described in
xentica.core.parameters
.
A minimal example, the CA where each cell is taking the mean value of its neighbors each step:
from xentica import core
from xentica.core import color_effects
class MeanCA(core.CellularAutomaton):
state = core.IntegerProperty(max_val=255)
class Topology:
dimensions = 2
lattice = core.OrthogonalLattice()
neighborhood = core.MooreNeighborhood()
border = core.TorusBorder()
def emit(self):
for i in range(len(self.buffers)):
self.buffers[i].state = self.main.state
def absorb(self):
s = core.IntegerVariable()
for i in range(len(self.buffers)):
s += self.neighbors[i].buffer.state
self.main.state = s / len(self.buffers)
@color_effects.MovingAverage
def color(self):
v = self.main.state
return (v, v, v)
-
class
xentica.core.base.
BSCA
¶ Bases:
type
The meta-class for
CellularAutomaton
.Prepares
main
,buffers
andneighbors
class variables being used inemit()
,absorb()
andcolor()
methods.-
mandatory_fields
= ('dimensions', 'lattice', 'neighborhood', 'border')¶
-
-
class
xentica.core.base.
CellularAutomaton
(experiment_class)¶ Bases:
xentica.core.base.Translator
The base class for all Xentica models.
Generates GPU kernels source code, compiles them, initializes necessary GPU arrays and populates them with the seed.
After initialization, you can run a step-by-step simulation and render the field at any moment:
from xentica import core import moire class MyCA(core.CellularAutomaton): # ... class MyExperiment(core.Experiment): # ... ca = MyCA(MyExperiment) ca.set_viewport((320, 200)) # run CA manually for 100 steps for i in range(100): ca.step() # render current timestep frame = ca.render() # or run the whole process interactively with Moire gui = moire.GUI(runnable=ca) gui.run()
Parameters: experiment_class – Experiment
instance, holding all necessary parameters for the field initialization.-
apply_speed
(dval)¶ Change the simulation speed.
Usable only in conduction with Moire, although you can use the
speed
value in your custom GUI too.Parameters: dval – Delta by which speed is changed.
-
load
(filename)¶ Load the CA state from
filename
file.
-
render
()¶ Render the field at the current timestep.
You must call
set_viewport()
before do any rendering.Returns: NumPy array of np.uint8
values,width * height * 3
size. The RGB values are consecutive.
-
save
(filename)¶ Save the CA state into
filename
file.
-
set_viewport
(size)¶ Set the viewport (camera) size and initialize GPU array for it.
Parameters: size – tuple with the width and height in pixels.
-
step
()¶ Perform a single simulation step.
timestep
attribute will hold the current step number.
-
toggle_pause
()¶ Toggle
paused
flag.When paused, the
step()
method does nothing.
-
-
class
xentica.core.base.
CachedNeighbor
¶ Bases:
object
The utility class, intended to hold the main and buffered CA state.
Experiments (xentica.core.experiment)¶
The collection of classes to describe experiments for CA models.
The experiment is a class with CA parameters stored as class
variables. Different models may have a different set of parameters. To
make sure all set correctly, you should inherit your experiments from
Experiment
class.
A quick example:
from xentica import core, seeds
class MyExperiment(core.Experiment):
# RNG seed string
word = "My Special String"
# field size
size = (640, 360, )
# initial field zoom
zoom = 3
# initial field shift
pos = [0, 0]
# A pattern used in initial board state generation.
# BigBang is a small area initialized with high-density random values.
seed = seeds.patterns.BigBang(
# position Big Bang area
pos=(320, 180),
# size of Big Bang area
size=(100, 100),
# algorithm to generate random values
vals={
"state": seeds.random.RandInt(0, 1),
}
)
-
class
xentica.core.experiment.
Experiment
¶ Bases:
object
The base class for all experiments.
Right now doing nothing, but will be improved in future versions. So it is advised to inherit your experiments from it.
Properties (xentica.core.properties)¶
The collection of classes to describe properties of CA models.
Warning
Do not confuse with Python properties.
Xentica properties are declaring as class variables and helping you to organize CA state into complex structures.
Each CellularAutomaton
instance should have at least one property declared. The property name
is up to you. If your model has just one value as a state (like in most
classic CA), the best practice is to call it state
as follows:
from xentica import core
class MyCA(core.CellularAutomaton):
state = core.IntegerProperty(max_val=1)
# ...
Then, you can use it in expressions of emit()
, absorb()
and
color()
functions as:
self.main.state
- to get and set main state;
self.buffers[i].state
- to get and set i-th buffered state;
self.neighbors[i].buffer.state
- to get and set i-th neighbor buffered state.
Xentica will take care of all other things, like packing CA properties into binary representation and back, storing and getting corresponding values from VRAM, etc.
Most of properties will return
DeferredExpression
on access, so you can use them safely in mixed expressions:
self.buffers[i].state = self.main.state + 1
-
class
xentica.core.properties.
Property
¶ Bases:
xentica.core.expressions.DeferredExpression
,xentica.core.mixins.BscaDetectorMixin
Base class for all properties.
It has a vast set of default functionality already in places. Though, you are free to re-define it all to implement really custom behavior.
-
best_type
¶ Get the type that suits best to store a property.
Returns: tuple representing best type: (bit_width, numpy_dtype, gpu_c_type)
-
bit_width
¶ Get the number of bits necessary to store a property.
Returns: Positive integer, a property’s bit width.
-
buf_num
¶ Get a buffer’s index, associated to a property.
-
calc_bit_width
()¶ Calculate the property’s bit width.
This is the method you most likely need to override. It will be called from
bit_width()
.Returns: Positive integer, the calculated property’s width in bits.
-
coords_declared
¶ Test if the coordinates variables are declared.
-
ctype
¶ Get the C type, based on the result of
best_type()
.Returns: C type that suits best to store a property.
-
declare_once
()¶ Generate the C code to declare a variable holding a cell’s state.
You must push the generated code to the BSCA via
self.bsca.append_code()
, then declare necessary stuff viaself.bsca.declare()
.You should also take care of skipping the whole process if things are already declared.
-
declared
¶ Test if the state variable is declared.
-
dtype
¶ Get the NumPy dtype, based on the result of
best_type()
.Returns: NumPy dtype that suits best to store a property.
-
nbr_num
¶ Get a neighbor’s index, associated to a property.
-
pack_cast
(expr)¶ Fix given expression for safe use with packing.
Parameters: expr – Any C expression. Returns: Fixed, correctly casted expression.
-
unpack_cast
(expr)¶ Fix given expression for safe use with unpacking.
Parameters: expr – Any C expression. Returns: Fixed, correctly casted expression.
-
width
¶ Get the number of memory cells to store a property.
In example, if
ctype == "int"
andbit_width == 64
, you need 2 memory cells.Returns: Positive integer, a property’s width.
-
-
class
xentica.core.properties.
IntegerProperty
(max_val)¶ Bases:
xentica.core.properties.Property
Most generic property for you to use.
It is just a positive integer with the upper limit of
max_val
.-
calc_bit_width
()¶ Calculate the bit width, based on
max_val
.
-
-
class
xentica.core.properties.
ContainerProperty
¶ Bases:
xentica.core.properties.Property
A property acting as a holder for other properties.
Currently is used only for inner framework mechanics, in particular, to hold, pack and unpack all top-level properties.
It will be enhanced in future versions, and give you the ability to implement nested properties structures.
Warning
Right now, direct use of this class is prohibited.
-
buf_num
¶ Get a buffer’s index, associated to a property.
-
calc_bit_width
()¶ Calculate the bit width as a sum of inner properties’ bit widths.
-
declare_once
()¶ Do all necessary declarations for inner properties.
Also, implements the case of the off-board neighbor access.
Parameters: init_val – The default value for the property.
-
deferred_write
()¶ Pack the state and write its value to the VRAM.
CellularAutomaton
calls this method at the end of the kernel processing.
-
nbr_num
¶ Get a neighbor’s index, associated to a property.
-
properties
¶ Get inner properties.
-
unpacked
¶ Test if inner properties are unpacked from the memory.
-
values
()¶ Iterate over properties, emulating
dict
functionality.
-
Parameters (xentica.core.parameters)¶
The collection of classes to describe the model’s meta-parameters.
The parameter is the value that influences the whole model’s behavior
in some way. Each parameter has a default value. Then you could
customize them per each experiment or change interactively using
Bridge
.
There are two types of parameters in Xentica:
- Non-interactive
- are constant during a single experiment run. The change of this
parameter is impossible without a whole model’s source code being
rebuilt. The engine makes sure those params are correctly defined
globally with the
#define
directive. So actually, even if you’ll change their values at runtime, it doesn’t affect the model in any way. - Interactive
- could be effectively changed at runtime, since engine traits them as extra arguments to CUDA kernels. That means, as long as you’ll set a new value to an interactive parameter, it will be passed into the kernel(s) at the next timestep. Be warned though: every parameter declared as interactive, will degrade the model’s performance further.
The example of parameters’ usage:
from xentica import core
from xentica.tools.rules import LifeLike
from examples.game_of_life import GameOfLife, GOLExperiment
class LifelikeCA(GameOfLife):
rule = core.Parameter(
default=LifeLike.golly2int("B3/S23"),
interactive=True,
)
def absorb(self):
# parent's clone with parameter instead of hardcoded rule
neighbors_alive = core.IntegerVariable()
for i in range(len(self.buffers)):
neighbors_alive += self.neighbors[i].buffer.state
is_born = (self.rule >> neighbors_alive) & 1
is_sustain = (self.rule >> 9 >> neighbors_alive) & 1
self.main.state = is_born | is_sustain & self.main.state
class DiamoebaExperiment(GOLExperiment):
rule = LifeLike.golly2int("B35678/S5678")
model = LifelikeCA(DiamoebaExperiment)
-
class
xentica.core.parameters.
Parameter
(default=0, interactive=False)¶ Bases:
xentica.core.mixins.BscaDetectorMixin
The implementation of Xentica meta-parameter.
Parameters: - default – The default value for the parameter to use when it’s omitted in the experiment class.
- interactive –
True
if the parameter could be safely changed at runtime (more details above in the module description).
-
ctype
¶ Get the parameter’s C type.
-
dtype
¶ Get the parameter’s NumPy type.
-
name
¶ Get the parameter’s name.
-
value
¶ Get the parameter’s value directly.
Variables (xentica.core.variables)¶
The collection of classes to declare and use C variables and constants.
If the logic of your emit()
, absorb()
or color()
functions
requires the intermediate variables, you must declare them via classes
from this module in the following way:
from xentica import core
class MyCA(core.CellularAutomaton):
# ...
def emit(self):
myvar = core.IntegerVariable()
Then you can use them in mixed expressions, like this:
myvar += self.neighbors[i].buffer.state
self.main.state = myvar & 1
You may also define constants or other #define
patterns with
Constant
class.
-
class
xentica.core.variables.
Constant
(name, value)¶ Bases:
xentica.core.mixins.BscaDetectorMixin
The class for defining constants and
#define
patterns.Once you instantiate
Constant
, you must feed it toCellularAutomaton.define_constant()
in order to generate the correct C code:const = Constant("C_NAME", "some_value") self.bsca.define_constant(const)
Parameters: - name – The name to use in
#define
. - value – A value for the define, it will be converted to a string
with
str()
.
-
get_define_code
()¶ Get the C code for
#define
.
-
name
¶ Get the name of the constant.
- name – The name to use in
-
class
xentica.core.variables.
Variable
(val=None, name='var')¶ Bases:
xentica.core.expressions.DeferredExpression
,xentica.core.mixins.BscaDetectorMixin
The base class for all variables.
Most of the functionality for variables is already implemented in it. Though, you are free to re-define it all to implement really custom behavior.
Parameters: - val – The initial value for the variable.
- name – Fallback name to declare the variable with.
-
code
¶ Get the variable name as a C code.
-
declare_once
()¶ Declare the variable and assign the initial value to it.
-
var_name
¶ Get the variable name.
-
class
xentica.core.variables.
IntegerVariable
(val='0', **kwargs)¶ Bases:
xentica.core.variables.Variable
The variable intended to hold a positive integer.
-
var_type
= 'unsigned int'¶ C type to use in definition.
-
-
class
xentica.core.variables.
FloatVariable
(val='0.0f', **kwargs)¶ Bases:
xentica.core.variables.Variable
The variable intended to hold a 32-bit float.
-
var_type
= 'float'¶ C type to use in definition.
-
Expressions (xentica.core.expressions)¶
The module holding base expressions classes.
-
class
xentica.core.expressions.
DeferredExpression
(code='')¶ Bases:
object
The base class for other classes intended to be used in mixed expressions.
In particular, it is used in base
Variable
andProperty
classes.Most of the magic methods dealing with binary and unary operators as well as augmented assigns are automatically overridden for this class. As a result, you can use its subclasses in mixed expressions with ordinary Python values. See the example in
variables
module description.- Allowed binary ops
+
,-
,*
,/
,%
,>>
,<<
,&
,^
,|
,<
,<=
,>
,>=
,==
,!=
- Allowed unary ops
+
,-
,~
,abs
,int
,float
,round
- Allowed augmented assigns
+=
,-=
,*=
,/=
,%=
,<<=
,>>=
,&=
,^=
,|=
Color Effects (xentica.core.color_effects)¶
The collection of decorators for the color()
method, each CA model
should have.
The method should be decorated by one of the classes below, otherwise the correct model behavior will not be guaranteed.
All decorators are get the (red, green, blue)
tuple from
color()
method, then process it to create some color effect.
A minimal example:
from xentica import core
from xentica.core import color_effects
class MyCA(core.CellularAutomaton):
state = core.IntegerProperty(max_val=1)
# ...
@color_effects.MovingAverage
def color(self):
red = self.main.state * 255
green = self.main.state * 255
blue = self.main.state * 255
return (red, green, blue)
-
class
xentica.core.color_effects.
ColorEffect
(func)¶ Bases:
xentica.core.mixins.BscaDetectorMixin
The base class for other color effects.
You may also use it as a standalone color effect decorator, it just doing nothing, storing the calculated RGB value directly.
To create your own class inherited from
ColorEffect
, you should override__call__
method, and place a code of the color processing intoself.effect
. The code should process values ofnew_r
,new_g
,new_b
variables and store the result back to them.An example:
class MyEffect(ColorEffect): def __call__(self, *args): self.effect = "new_r += 20;" self.effect += "new_g += 15;" self.effect += "new_b += 10;" return super().__call__(*args)
-
class
xentica.core.color_effects.
MovingAverage
(func)¶ Bases:
xentica.core.color_effects.ColorEffect
Apply the moving average to each color channel separately.
With this effect, 3 additional settings are available for you in
Experiment
classes:- fade_in
- The maximum delta by which a channel could increase its value in a single timestep.
- fade_out
- The maximum delta by which a channel could decrease its value in a single timestep.
- smooth_factor
- The divisor for two previous settings, to make the effect even smoother.
Renderers (xentica.core.renderers)¶
The collection of classes implementing render logic.
The renderer takes the array of cells’ colors and renders the screen frame from it. Also, it is possible to expand a list of user actions, adding ones specific to the renderer, like zoom, scroll, etc.
The default renderer is RendererPlain
. Though there are no
other renderers yet, you may try to implement your own and apply it to
CA model as follows:
from xentica.core import CellularAutomaton
from xentica.core.renderers import Renderer
class MyRenderer(Renderer):
# ...
class MyCA(CellularAutomaton):
renderer = MyRenderer()
# ...
-
class
xentica.core.renderers.
Renderer
¶ Bases:
xentica.core.mixins.BscaDetectorMixin
Base class for all renderers.
For correct behavior, renderer classes should be inherited from this class. Then at least
render_code()
method should be implemented.However, if you are planning to add user actions specific to your renderer, more methods should be overridden:
__init__()
, where you expand a list of kernel arguments inself.args
;get_args_vals()
, where you expand the list of arguments’ values;setup_actions()
, where you expand a dictionary of bridge actions;
See
RendererPlain
code as an example.-
get_args_vals
(bsca)¶ Get a list of kernel arguments’ values.
The order should correspond to
self.args
, with the values themselves as either PyCUDAGpuArray
or correct NumPy instance. Those values will be used directly as arguments to PyCUDA kernel execution.Parameters: bsca – xentica.core.CellularAutomaton
instance.
-
render_code
()¶ Generate C code for rendering.
At a minimum, it should process cells colors stored in
col
GPU-array, and store the resulting pixel’s value intoimg
GPU-array. It can additionally use other custom arguments if any of them set up.
-
setup_actions
(bridge)¶ Expand bridge with custom user actions.
You can do it as follows:
class MyRenderer(Renderer): # ... @staticmethod def my_awesome_action(): def func(ca, gui): # do something with ``ca`` and ``gui`` return func def setup_actions(self): bridge.key_actions.update({ "some_key": self.my_awesome_action(), })
Parameters: bridge – xentica.bridge.Bridge
instance.
-
class
xentica.core.renderers.
RendererPlain
(projection_axes=None)¶ Bases:
xentica.core.renderers.Renderer
Render board as 2D plain.
If your model has more than 2 dimensions, a projection over
projection_axes
tuple will be made. The default is two first axes, which corresponds to(0, 1)
tuple.-
static
apply_move
(bsca, *args)¶ Apply a move action to CA class.
Parameters: bsca – xentica.core.CellularAutomaton
instance.
-
static
apply_zoom
(bsca, dval)¶ Apply a zoom action to CA class.
Parameters: - bsca –
xentica.core.CellularAutomaton
instance. - dval – Delta by which the field is zoomed.
- bsca –
-
get_args_vals
(bsca)¶ Extend kernel arguments values.
-
static
move
(delta_x, delta_y)¶ Move over a game field by some delta.
Parameters: - dx – Delta by x-axis.
- dy – Delta by y-axis.
-
render_code
()¶ Implement the code for the render kernel.
-
setup_actions
(bridge)¶ Extend the bridge with the scroll and zoom user actions.
-
static
zoom
(dzoom)¶ Zoom a game field by some delta.
Parameters: dzoom – Delta by which field is zoomed.
-
static
Exceptions (xentica.core.exceptions)¶
The collection of exceptions, specific to the framework.
-
exception
xentica.core.exceptions.
XenticaException
¶ Bases:
Exception
The basic Xentica framework exception.
Mixins (xentica.core.mixins)¶
The collection of mixins to be used in core classes.
Would be interesting only if you are planning to hack into Xentica core functionality.
-
class
xentica.core.mixins.
BscaDetectorMixin
¶ Bases:
object
Add a functionality to detect
CellularAutomaton
class instances holding current class.-
bsca
¶ Get a
CellularAutomaton
instance holding current class.The objects tree is scanned up to the top and the first instance found is returned.
-
-
class
xentica.core.mixins.
DimensionsMixin
¶ Bases:
object
The base functionality for classes, operating on a number of dimensions.
Adds
dimensions
property to a class, and checks it automatically over a list of allowed dimensions.-
allowed_dimension
(num_dim)¶ Test if a particular dimensionality is allowed.
Parameters: num_dim – The number of dimensions to test. Returns: Boolean value, either dimensionality is allowed or not.
-
dimensions
¶ Get a number of dimensions.
-
supported_dimensions
= []¶ A list of integers, containing a supported dimensionality. You must set it manually for every class using
DimensionsMixin
.
-