utility_programming/
lib.rs

1//! # Utility-Programming: A library for composable utility programming.
2//!
3//! This library is brought to you by
4//! the [AdvancedResearch](https://github.com/advancedresearch/advancedresearch.github.io) community.
5//!
6//! ### Introduction
7//!
8//! Utility programming can be thought of as a form of programming with soft constraints.
9//! Instead of programming rules explicitly, various aspects of the solution is assigned a utility.
10//! An optimization algorithm then generates and modifies objects to maximize the utility.
11//!
12//! The advantage of utility programming is that one feature can be traded with another.
13//! By adjusting the weights of utility of various features, the optimized object can
14//! be modified to fit new criteria without rewriting the algorithm.
15//!
16//! Utility programming differs from other kinds of machine learning in the following ways:
17//!
18//! - The goal is to develop reusable composable components based on a common set of abstractions
19//! - The utility of sub-components are often used in the strategy of higher level components
20//! - Utilities are not treated as final goals, but as a technique for optimization and learning
21//! - The objects to be optimized are virtual
22//! - The optimization process is assumed to a have few side effects
23//!
24//! These properties results in higher focus on the programming aspects by adjusting utilities.
25//!
26//! From a library perspective, finding utility definitions that interact nicely with
27//! each other is the challenge of API designers.
28//!
29//! ### Design
30//!
31//! The abstractions for utility programming consists of 3 parts:
32//!
33//! - `Utility` trait (implemented by objects that measure some utility)
34//! - `Generator` trait (implemented by objects that generate some kind of object)
35//! - `Modifier` trait (implemented by objects that modify other objects)
36//!
37//! All traits are implemented for `Vec<T>` where `T` implements the trait:
38//!
39//! - `Vec<T: Utility>` sums the utility of each sub-utility
40//! - `Vec<T: Generator>` picks a random generator to generate the object
41//! - `Vec<T: Modifier>` picks a random modifier to modify the object
42//!
43//! It is common to use `enum` instead of `struct` to combine variants with `Vec<T>`.
44//!
45//! Modification requires `undo` and `redo` for backtracking and replication.
46//!
47//! ### Utility Epistomology
48//!
49//! Epistomology is philosophy of knowledge.
50//! Utility Epistomology means philosophy of utility knowledge.
51//!
52//! Utility optimization consists of 3 major efficiency levels:
53//!
54//! 1. Object generation (blind utility)
55//! 2. Modification (target utility)
56//! 3. Trade-off prediction (heuristics utility)
57//!
58//! The optimized solution is a result of the 3 levels above.
59//! All the information that is used to evaluate the utility
60//! is stored in the object of interest.
61//! This means one can predict certain aspects of the optimal solution
62//! from the utility parameters, generators and modifiers.
63//!
64//! An optimal agent operating at the 1st level is required behavior to be optimally creative.
65//! It is able to think of anything that might be possible for a given problem.
66//!
67//! An optimal agent operating at the 2nd level is required behavior to be instrumentally rational.
68//! It is able to maximize its goal in the most efficient way.
69//!
70//! An optimal agent operating at the 3rd level is required behavior to be zen rational.
71//! It is able to predict what the optimized solution might look like if the utility was different.
72//!
73//! One motivation for developing tools for utility programming is to study
74//! [Zen Rationality](https://github.com/advancedresearch/path_semantics/blob/master/papers-wip/zen-rationality.pdf).
75
76extern crate rand;
77
78/// Implemented by objects that measure utility of an object.
79pub trait Utility<T> {
80    /// Computes the utility of an object.
81    fn utility(&self, obj: &T) -> f64;
82}
83
84/// Sums up utility from multiple sub-terms.
85impl<T, U: Utility<T>> Utility<T> for Vec<U> {
86    fn utility(&self, obj: &T) -> f64 {
87        self.iter().map(|it| it.utility(obj)).sum()
88    }
89}
90
91/// Implemented by objects that generates other objects.
92pub trait Generator {
93    /// The type of the object generated.
94    type Output;
95    /// Generate a new object.
96    ///
97    /// This might be indeterministic.
98    fn generate(&mut self) -> Self::Output;
99}
100
101impl<T: Generator> Generator for Vec<T> {
102    type Output = T::Output;
103    fn generate(&mut self) -> Self::Output {
104        let index = rand::random::<usize>() % self.len();
105        self[index].generate()
106    }
107}
108
109/// Modifies objects in a way that can be reversed.
110///
111/// ### Change in meaning of modifier
112///
113/// When there are multiple modifiers in the same context,
114/// such as a list of modifiers, then one modification might
115/// change the meaning of another.
116///
117/// For example, when an item is insert into a list:
118///
119/// - `[0, 3]`, inserting at `1` changes to `[0, 4]` (range includes index)
120/// - `[2, 4]`, inserting at `0` changes to `[3, 5]` (range is after index)
121/// - `[0, 3]`, inserting at `3` changes to `[0, 3]` (the same)
122///
123/// Meaning of a modifier is information that refers to information in the object.
124/// When the object changes, the consistency of the reference might require updating the modifier.
125///
126/// This is what the methods `undo_meaning` and `redo_meaning` do.
127/// They preserve meaning even though the change originated from another modifier.
128pub trait Modifier<T> {
129    /// The change applied to an object.
130    type Change;
131    /// Modify an object and return the change.
132    ///
133    /// This might be indeterministic.
134    /// Use `redo_meaning` for applying change in meaning of modifier.
135    fn modify(&mut self, obj: &mut T) -> Self::Change;
136    /// Undo change made to an object.
137    ///
138    /// Required to be deterministic.
139    fn undo(&mut self, change: &Self::Change, obj: &mut T);
140    /// Redo change made to an object.
141    ///
142    /// Required to be deterministic.
143    fn redo(&mut self, change: &Self::Change, obj: &mut T);
144    /// Undo meaning change in the modifier introduced by a change.
145    ///
146    /// This is called after undoing change by any modifier used in same context.
147    fn undo_meaning(&mut self, _change: &Self::Change) {}
148    /// Redo meaning change in the modifier.
149    ///
150    /// This is called after modification by any modifier used in same context.
151    fn redo_meaning(&mut self, _change: &Self::Change) {}
152}
153
154impl<T, U: Modifier<T>> Modifier<T> for Vec<U> {
155    type Change = (usize, U::Change);
156    fn modify(&mut self, obj: &mut T) -> Self::Change {
157        let index = rand::random::<usize>() % self.len();
158        (index, self[index].modify(obj))
159    }
160    fn undo(&mut self, change: &Self::Change, obj: &mut T) {
161        self[change.0].undo(&change.1, obj)
162    }
163    fn redo(&mut self, change: &Self::Change, obj: &mut T) {
164        self[change.0].redo(&change.1, obj)
165    }
166    fn undo_meaning(&mut self, change: &Self::Change) {
167        for it in self {it.undo_meaning(&change.1)}
168    }
169    fn redo_meaning(&mut self, change: &Self::Change) {
170        for it in self {it.undo_meaning(&change.1)}
171    }
172}
173
174/// Modifies an object using a modifier by maximizing utility.
175pub struct ModifyOptimizer<M, U> {
176    /// The modifier to modify the object.
177    pub modifier: M,
178    /// The measured utility.
179    pub utility: U,
180    /// The number of tries before giving up.
181    pub tries: usize,
182    /// The number of repeated modifications before backtracking.
183    pub depth: usize,
184}
185
186impl<T, M, U> Modifier<T> for ModifyOptimizer<M, U>
187    where M: Modifier<T>, U: Utility<T>, M::Change: Clone
188{
189    type Change = Vec<M::Change>;
190    fn modify(&mut self, obj: &mut T) -> Self::Change {
191        let mut best = vec![];
192        let mut best_utility: f64 = self.utility.utility(obj);
193        let mut stack = vec![];
194        for _ in 0..self.tries {
195            for _ in 0..self.depth {
196                let change = self.modifier.modify(obj);
197                self.modifier.redo_meaning(&change);
198                stack.push(change);
199                let utility = self.utility.utility(obj);
200                if best_utility < utility {
201                    best = stack.clone();
202                    best_utility = utility;
203                }
204            }
205            while let Some(ref action) = stack.pop() {
206                self.modifier.undo(action, obj);
207                self.modifier.undo_meaning(&action);
208            }
209        }
210        for i in 0..best.len() {
211            self.modifier.redo(&best[i], obj);
212            self.modifier.redo_meaning(&best[i]);
213        }
214        best
215    }
216    fn undo(&mut self, change: &Self::Change, obj: &mut T) {
217        for i in (0..change.len()).rev() {
218            self.modifier.undo(&change[i], obj);
219            self.modifier.undo_meaning(&change[i]);
220        }
221    }
222    fn redo(&mut self, change: &Self::Change, obj: &mut T) {
223        for i in 0..change.len() {
224            self.modifier.redo(&change[i], obj);
225            self.modifier.redo_meaning(&change[i]);
226        }
227    }
228}