---
id: creating-random-operators
title: Creating random assignment operators
layout: docs
permalink: /docs/creating-random-operators.html
prev: creating-new-operators.html
---

New random operators can be created by extending the `PlanOutOpRandom` class,
and implementing a `simpleExecute()` and `options()` method.
This class is the workhorse of PlanOut's random assignment capabilities.
Random assignments are done primarily through two built-in helper methods that come with `PlanOutOpRandom`:

 - `getHash()` gets a random integer between `0` and a very large number (`0xFFFFFFFFFFFFFFF`), inclusive.
 Many random assignment procedures will mod the integer returned by
 this method to perform some type of random assignment.
 - `getUniform()` gets a floating point number between some range of values.
This number should be uniform across the specified minimum and maximum value (default values are 0.0 and 1.0).

Hashes and numbers generated by these methods use the experiment-level,
variable-level, and unit-level salts, as described in the
 [How PlanOut Works guide](how-planout-works.html). 
They may also take optional argument, `appended_unit`, which can
either be a scalar (e.g., integer or string) or list value
(consisting of scalars). This appended unit may be used to generate
multiple independent random numbers.

## Examples
We use examples to show how implementing random operators works in practice.

### UniformChoice
UniformChoice picks an element from a given input list by generating a random integer and modding it by the appropriate range.

```python
class UniformChoice(PlanOutOpRandom):
  def options(self):
    return {'choices': {'required': 1, 'description': 'elements to draw from'}}

   def simpleExecute(self):
     choices = self.args.get('choices')
     if len(choices) == 0:
       return []
     rand_index = self.getHash() % len(choices)
     return choices[rand_index]
```

Here, we can see that the operator has one required argument, `choices`.  The `simpleExecute()` method grabs this input parameter from `self.parameters`, and then generates a random index by calling `self.getHash()`.

### RandomFloat
RandomFloat generates a random floating point number between two values.

```python
class RandomFloat(PlanOutOpRandom):
  def options(self):
    return {
      'min': {'required': 0, 'description': 'min (float) value drawn'},
      'max': {'required': 0, 'description': 'max (float) value being drawn'}}

   def simpleExecute(self):
     min_val = self.args.get('min', 0)
     max_val = self.args.get('max', 1)
     return self.getUniform(min_val, max_val)
```
`RandomFloat` takes two optional parameters, 'min' and 'max', which it passes into `self.getUniform` to generate a uniform random number. 

### Generating normally distributed numbers
So far we have only looked at examples where random assignment is done by
generating a single random number.
Many operations, however, involve drawing multiple random numbers.
For example, [normally distributed numbers can be generated](http://en.wikipedia.org/wiki/Box-Muller_transform) in a pseudo-random
fashion using two independent uniformly distributed random numbers:

```python
class Normal(PlanOutOpRandom):
   def options(self):
    return {
      'mean': {'required': 1, 'description': 'mean value drawn'},
      'sd': {'required': 1, 'description': 'standard deviation of normal'}}

 def options():
  def simpleEvaluate():
     mean = self.args.get('mean')
     sd = self.args.get('sd')

     # Use the Box-Muller transform to generate a normal from two
     # independent draws from a Uniform[0,1] distribution
     # see http://en.wikipedia.org/wiki/Box-Muller_transform
     theta = 2.0 * math.pi * self.getUniform(0.0, 1.0, 'theta')
     rho = sqrt(-2.0 * log(1.0 - self.getUniform(0.0, 1.0, 'rho')))
     return mean + sd * rho * math.cos(theta)
```

In the code above, we use the optional appended_unit parameter (the third parameter to `getUniform()`) to make multiple draws from the uniform distribution to generate a normally distributed floating point number, using the Box-Muller transform.
