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.

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 neighbors. Then, at each step, the interaction between cells are performed via buffers in 2-phase emit/absorb process. More detailed description of 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 cell’s buffers with corresponding values, depending on cell’s main state and (optionally) on neighbors’ main states. The most easy logic is to just copy the main state to buffers. It is esspecially useful when you’re intending to emulate classic CA (like Conway’s Life) with BSCA. Write access to 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 creation. For the further instructions on how to use cell’s main and buffered states, see xentica.core.properties, for the instructions on variables and expressions with them, see xentica.core.variables.

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

Meta-class for CellularAutomaton.

Performs all necessary stuff to generate GPU kernels given class definition.

It is also preparing main, buffers and neighbors class variables being used in emit(), absorb() and color() methods.

append_code(code)

Append code to kernel’s C code.

build_absorb()

Generate absorb() kernel.

Returns:String with C code for absorb() kernel.
build_defines()

Generate #define section for all kernels.

Returns:String with C code with necessary defines.
build_emit()

Generate emit() kernel.

Returns:String with C code for emit() kernel.
build_render()

Generate render() kernel.

Returns:String with C code for render() kernel.
coords_declared

Check if coordinate variables are declared.

Returns:True if coordinate variables are declared, False otherwise.
declare(prop)

Mark property declared.

Parameters:propProperty subclass instance.
declare_coords()

Mark coordinate variables declared.

deferred_write(prop)

Declare a property for deferred write.

The property will be written into a memory at the end of C code generation.

Parameters:propProperty subclass instance.
define_constant(constant)

Remember the constant is defined.

Parameters:constantConstant instance.
index_to_coord(i)

Wrap lattice.index_to_coord method.

Parameters:i – Cell’s index.
is_constant(constant)

Check if the constant is defined.

Parameters:constantConstant instance.
Returns:True if constant is defined, False otherwise.
is_declared(prop)

Check if prop property is declared.

Parameters:propProperty subclass instance.
Returns:True if property is declared, False otherwise.
is_unpacked(prop)

Check if prop property is unpacked.

Parameters:propProperty subclass instance.
Returns:True if property is unpacked, False otherwise.
pack_state(state)

Pack state structure into raw in-memory representation.

Returns:Integer representing packed state.
unpack(prop)

Mark prop property unpacked.

Parameters:propProperty subclass instance.
class xentica.core.base.CellularAutomaton(experiment_class)

Bases: object

Base class for all Xentica models.

Compiles GPU kernels generated by BSCA metaclass, initializes necessary GPU arrays and popupates them with the seed.

After initialization, you can run 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_classExperiment 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 viewport (camera) size and initialize GPU array for it.

Parameters:size – tuple with 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

Utility class, intended to hold main and buffered CA state.

Experiments (xentica.core.experiment)

The collection of classes to describe experiments for CA models.

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 correct, you should inherit your experiments from Experiment class.

The 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

Base class for all experiments.

Right now doing nothing, but will be improved in future versions. So it is adviced 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 for 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.variables.DeferredExpression

Base class for all properties.

Has a vast set of default functionality already implemented. Though, you are free to re-define it all to implement really custom behavior.

best_type

Get 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.
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, calculated property’s width in bits.
coords_declared

Test if the coordinates variables are declared.

ctype

Get C type, based on result of best_type().

Returns:C type that suits best to store a property.
declare_once()

Generate C code to declare a variable holding cell’s state.

You must push the generated code to BSCA via self._bsca.append_code(), then declare necessary stuff via self._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 NumPy dtype, based on result of best_type().

Returns:NumPy dtype that suits best to store a property.
set_bsca(bsca, buf_num, nbr_num)

Set up a reference to BSCA instance.

Do not override this method, it is cruicial to inner framework mechanics.

Parameters:
  • bscaCellularAutomaton instance.
  • buf_num – Buffer’s index, associated to property.
  • nbr_num – Neighbor’s index, associated to property.
width

Get the number of memory cells to store a property.

In example, if ctype == "int" and bit_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 upper limit of max_val.

calc_bit_width()

Calculate 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.

calc_bit_width()

Calculate bit width as sum of inner properties’ bit widths.

declare_once(init_val=None)

Do all necessary declarations for inner properties.

Also, implements the case of off-board neighbor access.

Parameters:init_val – Default value for the property.
deferred_write()

Pack state and write its value to VRAM.

This method is called from BSCA at the end of kernel processing.

set_bsca(bsca, buf_num, nbr_num)

Propagate BSCA setting to inner properties.

unpacked

Test if inner properties are unpacked from memory.

values()

Iterate over properties, emulating dict functionality.

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:

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.DeferredExpression(code='')

Bases: object

Base class for other classes intended to be used in mixed expressions.

In particular, it is used in base Variable and Property 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 module description above.

Allowed binary ops
+, -, *, /, %, >>, <<, &, ^, |, <, <=, >, >=, ==, !=
Allowed unary ops
+, -, ~, abs, int, float, round
Allowed augmented assigns
+=, -=, *=, /=, %=, <<=, >>=, &=, ^=, |=
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 to BSCA.define_constant() in order to generate correct C code:

const = Constant("C_NAME", "bsca_var")
self._bsca.define_constant(const)
Parameters:
  • name – Name to use in #define.
  • value – String, evaluating into bsca.<value>, second part of #define.
get_define_code()

Get the C code for #define.

name

Get the name of constant.

replace_value(source)

Replace the constant’s value in generated C code.

Parameters:source – Generated C code.
class xentica.core.variables.Variable(val=None)

Bases: xentica.core.variables.DeferredExpression, xentica.core.mixins.BscaDetectorMixin

Base class for all variables.

Most of the functionality for variables are already implemented in it. Though, you are free to re-define it all to implement really custom behavior.

Parameters:val – Initial value for the variable.
code

Get the variable name as code.

var_name

Get variable name.

class xentica.core.variables.IntegerVariable(val=0)

Bases: xentica.core.variables.Variable

The variable intended to hold a positive integer.

var_type = 'unsigned int'

C type to use in definition.

Color Effects (xentica.core.color_effects)

The collection of decorators for color() method, each CA model should have.

The method should be decorated by one of the classes below, otherwise the correct model behavior is not guaranteed.

All decorators are get the (red, green, blue) tuple from color() method, then process it to create some color effect.

The 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

Base class for other color effects.

You may also use it as standalone color effect decorator, it just doing nothing, storing calculated RGB value directly.

To create your own class inherited from ColorEffect, you should override __call__ method, and place a code of color processing into self.effect. The code should process a values of new_r, new_g, new_b variables and store the result back to them.

The 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(MyEffect, self).__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 in self.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 PyCUDA GpuArray or correct NumPy instance. Those values will be used directly as arguments to PyCUDA kernel execution.

Parameters:bscaxentica.core.CellularAutomaton instance.
render_code()

Generate C code for rendering.

At minimum, it should process cells colors stored in col GPU-array, and store the resulting pixel’s value into img GPU-array. It can additionally use other custom arguments, if any 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:bridgexentica.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 field move action to CA class.

Parameters:bscaxentica.core.CellularAutomaton instance.
static apply_zoom(bsca, dval)

Apply field zoom action to CA class.

Parameters:
  • bscaxentica.core.CellularAutomaton instance.
  • dval – Delta by which field is zoomed.
get_args_vals(bsca)

Extend kernel arguments values.

static move(dx, dy)

Move over game field by some delta.

Parameters:
  • dx – Delta by x-axis.
  • dy – Delta by y-axis.
render_code()

Implement the code for render kernel.

setup_actions(bridge)

Extend bridge with scroll and zoom user actions.

static zoom(dzoom)

Zoom game field by some delta.

Parameters:dzoom – Delta by which field is zoomed.

Exceptions (xentica.core.exceptions)

The collection of exceptions, specific to the framework.

exception xentica.core.exceptions.XenticaException

Bases: Exception

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 functionlality to detect BSCA class instances holding current class.

All methods are for private use only.