Experiments with ruby-processing (processing-2.2.1) and JRubyArt for processing-3.0

Showing posts with label map. Show all posts
Showing posts with label map. Show all posts

Sunday, 12 October 2014

Transducers in ruby-processing

Transducers may be useful in ruby-processing for dealing with all sorts of algorithmic stuff. but in many cases if your brain can take it map, might be equally good. Here is example substituting a regular map with a transducer, I will have to see if I can find a more useful example:-

                                                            
require 'transducers'                                                                
                                                                                     
class GameOfLife < Processing::App                                                
  T = Transducers                                                                    
                                                                                      
  def random_data                                                                    
    T.transduce(T.map{ rand(1000) < ALIVE_START }, :<<, [], 0..row * column)
  end                                                                                
end                                                                                  

Thursday, 14 August 2014

Experimental map1d method JRubyArt (ruby-processing development)

The recently updated JRubyArt has now removed the processing convenience functions that were deprecated in ruby-processing-2.5.1. Other functions such as map are retained, but I wondered would a map1d range method be a more ruby like alternative:-
# @param [float] val
# @param [range(start1 .. end1)] r_in
# @param [range(start2 .. end2)] r_out
# @return [float] mapped value
#
def map1d(val, r_in, r_out)
  r_out.first + (r_out.last - r_out.first) * ((val - r_in.first).to_f / (r_in.last - r_in.first))
end

# usage map1d(5, (0 .. 10), (10 .. 110))           exclude end is false

puts map1d(5, (0 .. 10), (10 .. 110))

# output 60.0

Here is the method in action
# Mandelbrot Set example
# by Jordan Scales (http://jordanscales.com)
# 27 Dec 2012
# Modified to use map1d (instead of map), and somewhat 
# optimized (update_pixels instead of set, and hypot for abs)

# default size 900x600
# no need to loop
def setup
  size 900, 600
  load_pixels
  no_loop
end

# main drawing method
def draw
  (0 ... 900).each do |x|
    (0 ... 600).each do |y|
      c = Complex.new(map1d(x, (0 .. 900), (-3 .. 1.5)), map1d(y, (0 .. 600), (-1.5 .. 1.5)))
      # mandel will return 0 to 20 (20 is strong)
      #   map this to 0, 255 (and flip it)
      pixels[x + y * 900] = color(255 - map1d(mandel(c, 20), (0 .. 20), (0 .. 255)).to_i)
    end
  end
  update_pixels
end

# calculates the "accuracy" of a given point in the mandelbrot set
#    : how many iterations the number survives without becoming chaotic
def mandel(z, max = 10)
  score = 0
  c = z.clone
  while score < max 
    # z = z^2 + c
    z.square
    z.add c
    break if z.abs > 2

    score += 1
  end

  score
end


# rolled my own Complex class
#   stripped of all functionality, except for what I needed (abs, square, add, to_s)
#   
#   Using this class, runs in ~12.5s on my MacBook Air
#     compared to ~22s using ruby's Complex struct
class Complex

  attr_accessor :real, :imag

  def initialize(real, imag)
    @real = real
    @imag = imag
  end

  # square a complex number - overwriting it
  def square
    r = real * real - imag * imag
    i = 2 * real * imag

    @real = r
    @imag = i
  end

  # add a given complex number
  def add(c)
    @real += c.real
    @imag += c.imag
  end

  # compute the magnitude
  def abs
    hypot(real, imag)
  end

  def to_s
    "#{real} + #{imag}i"
  end

end

PS: I have not tested whether Jordans findings re: ruby complex implementation still hold true (jruby truffle claims wicked fast mandelbrot calculations). Benchmarking is tricky with jvm warmup up time, I have replaced sqrt(real * real + imag * imag) (abs method) with hypot(real, imag), which should be more efficient...

Tuesday, 4 March 2014

Getting closer to "functional programming with ruby-processing"

In a previous blog I reported that Dan Shiffmans Nature of Code had been ported to ruby processing. So I took a look at the code, and could not help from meddling here is my modified version of the perceptron sketch:-
# The Nature of Code
# http://natureofcode.com

# Simple Perceptron Example
# See: http://en.wikipedia.org/wiki/Perceptron

class Perceptron
  attr_reader :weights

  # Perceptron is created with n weights and a learning constant
  def initialize(n, c)
    @weights = Array.new(n) { rand(-1.0 .. 1) }
    @c = c # learning constant
  end

  # Function to train the Perceptron
  # Weights are adjusted based on "desired" answer
  def train(inputs, desired)
    # Guess the result
    guess = feedforward(inputs)
    # Compute the factor for changing the weight based on the error
    # Error = desired output - guessed output
    # Note this can only be 0, -2, or 2
    # Multiply by learning constant
    error = desired - guess
    # Adjust weights based on weightChange * input
    @weights.each_index{|i| @weights[i] += error * inputs[i]}
  end

  # Guess -1 or 1 based on input values
  def feedforward(inputs)
    # Sum all values
    sum = @weights.zip(inputs).map{|x, y| x * y}.inject(0){|x, y| x + y}
    # Result is sign of the sum, -1 or 1
    activate(sum)
  end

  def activate(sum)
    sum > 0 ? 1 : -1
  end
end

# A class to describe a training point
# Has an x and y, a "bias" (1) and a known output
# Could also add a variable for "guess" but not required here

class Trainer
  attr_reader :inputs, :answer

  def initialize(x, y, a)
    @inputs = [x, y, 1]
    @answer = a
  end
end

# Code based on text "Artificial Intelligence", George Luger

# The function to describe a line
def f(x)
  0.4 * x + 1
end

def setup
  size(640, 360)
  # Coordinate space
  @xmin = -400
  @ymin = -100
  @xmax =  400
  @ymax =  100

  @count = 0

  # The perceptron has 3 inputs -- x, y, and bias
  # Second value is "Learning Constant"
  @ptron = Perceptron.new(3, 0.00001)  # Learning Constant is low just b/c it's fun to watch, this is not necessarily optimal

   #Create a random set of training points and calculate the "known" answer
  @training = Array.new(2000) do
    x = rand(@xmin .. @xmax)
    y = rand(@ymin .. @ymax)
    answer = y < f(x) ? -1 : 1
    Trainer.new(x, y, answer)
  end

  smooth
end

def draw
  background(255)
  translate(width/2, height/2)

  # Draw the line
  stroke_weight(4)
  stroke(127)
  x1 = @xmin
  y1 = f(x1)
  x2 = @xmax
  y2 = f(x2)
  line(x1, y1, x2, y2)

  # Draw the line based on the current weights
  # Formula is weights[0]*x + weights[1]*y + weights[2] = 0
  stroke(0)
  stroke_weight(1)
  weights = @ptron.weights
  x1 = @xmin
  y1 = (-weights[2] - weights[0]*x1) / weights[1]
  x2 = @xmax
  y2 = (-weights[2] - weights[0]*x2) / weights[1]
  line(x1, y1, x2, y2)

  # Train the Perceptron with one "training" point at a time
  @ptron.train(@training[@count].inputs, @training[@count].answer)
  @count = (@count + 1) % @training.size

  # Draw all the points based on what the Perceptron would "guess"
  # Does not use the "known" correct answer
  @count.times do |i|
    stroke(0)
    stroke_weight(1)
    fill(0)
    train = @training[i]
    guess = @ptron.feedforward(train.inputs)
    no_fill if guess > 0

    ellipse(train.inputs[0], train.inputs[1], 8, 8)
  end
end

Here I am particulary pleased with the Perceptron feedforward code, that sums the weights using zip, map and inject. This code has a somewhat functional feel to it?

Followers

About Me

My photo
I have developed JRubyArt and propane new versions of ruby-processing for JRuby-9.1.5.0 and processing-3.2.2