///|
/// Common scalar targets used by value-based and policy-gradient examples.
pub struct TargetBatch {
values : Array[Double]
mask : Array[Bool]
}
///|
pub fn TargetBatch::values(self : TargetBatch) -> Array[Double] {
self.values
}
///|
pub fn TargetBatch::mask(self : TargetBatch) -> Array[Bool] {
self.mask
}
///|
pub fn TargetBatch::length(self : TargetBatch) -> Int {
self.values.length()
}
///|
pub fn discounted_sum(rewards : Array[Double], gamma : Double) -> Double {
let mut result = 0.0
let mut power = 1.0
for reward in rewards {
result = result + power * reward
power = power * gamma
}
result
}
///|
pub fn discounted_prefixes(
rewards : Array[Double],
gamma : Double,
) -> Array[Double] {
let result : Array[Double] = []
let mut running = 0.0
let mut i = rewards.length()
while i > 0 {
i = i - 1
running = rewards[i] + gamma * running
result.push(running)
}
let ordered : Array[Double] = []
let mut j = result.length()
while j > 0 {
j = j - 1
ordered.push(result[j])
}
ordered
}
///|
pub fn td_targets(
rewards : Array[Double],
next_values : Array[Double],
terminals : Array[Bool],
gamma : Double,
) -> Array[Double] {
let result : Array[Double] = []
let mut i = 0
while i < rewards.length() {
let bootstrap = if terminals[i] { 0.0 } else { gamma * next_values[i] }
result.push(rewards[i] + bootstrap)
i = i + 1
}
result
}
///|
pub fn generalized_advantages(
rewards : Array[Double],
values : Array[Double],
next_values : Array[Double],
terminals : Array[Bool],
gamma : Double,
lambda : Double,
) -> Array[Double] {
let result : Array[Double] = []
let mut running = 0.0
let mut i = rewards.length()
while i > 0 {
i = i - 1
let continuation = if terminals[i] { 0.0 } else { gamma * next_values[i] }
let delta = rewards[i] + continuation - values[i]
running = delta +
(if terminals[i] { 0.0 } else { gamma * lambda * running })
result.push(running)
}
let ordered : Array[Double] = []
let mut j = result.length()
while j > 0 {
j = j - 1
ordered.push(result[j])
}
ordered
}
///|
pub fn normalize_values(values : Array[Double]) -> Array[Double] {
let result : Array[Double] = []
if values.length() == 0 {
return result
}
let mut sum = 0.0
for value in values {
sum = sum + value
}
let mean = sum / values.length().to_double()
let mut squared = 0.0
for value in values {
let distance = value - mean
squared = squared + distance * distance
}
let variance = squared / values.length().to_double()
let scale = if variance <= 1.0e-12 { 1.0 } else { variance }
for value in values {
result.push((value - mean) / scale)
}
result
}
///|
pub fn clip_values(
values : Array[Double],
minimum : Double,
maximum : Double,
) -> Array[Double] {
let result : Array[Double] = []
for value in values {
if value < minimum {
result.push(minimum)
} else if value > maximum {
result.push(maximum)
} else {
result.push(value)
}
}
result
}
///|
pub fn scale_rewards(rewards : Array[Double], scale : Double) -> Array[Double] {
let result : Array[Double] = []
for reward in rewards {
result.push(reward * scale)
}
result
}
///|
pub fn target_batches(
values : Array[Double],
batch_size : Int,
) -> Array[TargetBatch] {
let result : Array[TargetBatch] = []
if batch_size <= 0 {
return result
}
let mut start = 0
while start < values.length() {
let batch : Array[Double] = []
let mask : Array[Bool] = []
let mut offset = 0
while offset < batch_size {
let index = start + offset
if index < values.length() {
batch.push(values[index])
mask.push(true)
} else {
batch.push(0.0)
mask.push(false)
}
offset = offset + 1
}
result.push({ values: batch, mask })
start = start + batch_size
}
result
}