///|
pub(all) struct FeatureProfile {
name : String
value_kind : String
domain_size : Int
mutable_values : Int
current_index : Int
cost : Int
locked : Bool
referenced : Bool
duplicate_values : Int
} derive(Debug, Eq)
///|
pub(all) struct ModelProfile {
policy_name : String
features : Array[FeatureProfile]
goal_variables : Array[String]
expression_nodes : Int
expression_depth : Int
comparisons : Int
mutable_features : Int
locked_features : Int
zero_cost_mutable : Int
singleton_domains : Int
duplicate_domain_values : Int
search_space : Int
} derive(Debug, Eq)
///|
pub fn Policy::profile(self : Policy) -> ModelProfile {
let stats = self.goal.stats()
let features : Array[FeatureProfile] = []
let mut mutable_features = 0
let mut locked_features = 0
let mut zero_cost_mutable = 0
let mut singleton_domains = 0
let mut duplicate_domain_values = 0
for item in self.features {
let duplicate_values = count_duplicate_values(item.domain)
duplicate_domain_values = duplicate_domain_values + duplicate_values
if item.locked {
locked_features = locked_features + 1
} else {
mutable_features = mutable_features + 1
if item.cost == 0 && item.domain.length() > 1 {
zero_cost_mutable = zero_cost_mutable + 1
}
}
if item.domain.length() <= 1 {
singleton_domains = singleton_domains + 1
}
features.push({
name: item.name,
value_kind: item.current.kind(),
domain_size: item.domain.length(),
mutable_values: feature_mutable_values(item),
current_index: feature_current_index(item),
cost: item.cost,
locked: item.locked,
referenced: stats.variables.contains(item.name),
duplicate_values,
})
}
{
policy_name: self.name,
features,
goal_variables: stats.variables,
expression_nodes: stats.nodes,
expression_depth: stats.depth,
comparisons: stats.comparisons,
mutable_features,
locked_features,
zero_cost_mutable,
singleton_domains,
duplicate_domain_values,
search_space: self.search_space_size(),
}
}
///|
fn feature_mutable_values(item : Feature) -> Int {
if item.locked {
0
} else {
let mut count = 0
for candidate in item.domain {
if candidate != item.current {
count = count + 1
}
}
count
}
}
///|
fn feature_current_index(item : Feature) -> Int {
for index, value in item.domain {
if value == item.current {
return index
}
}
-1
}
///|
fn count_duplicate_values(values : Array[Value]) -> Int {
let mut duplicates = 0
for index, value in values {
let mut seen = false
for previous in 0.. Bool {
!self.locked && self.mutable_values > 0 && self.referenced
}
///|
pub fn FeatureProfile::label(self : FeatureProfile) -> String {
if self.locked {
"locked"
} else if self.mutable_values == 0 {
"constant"
} else if !self.referenced {
"unreferenced"
} else if self.cost == 0 {
"free"
} else {
"actionable"
}
}
///|
pub fn ModelProfile::actionable_features(self : ModelProfile) -> Array[String] {
let result : Array[String] = []
for item in self.features {
if item.actionable() {
result.push(item.name)
}
}
result
}
///|
pub fn ModelProfile::unreferenced_features(
self : ModelProfile,
) -> Array[String] {
let result : Array[String] = []
for item in self.features {
if !item.referenced {
result.push(item.name)
}
}
result
}
///|
pub fn ModelProfile::locked_referenced_features(
self : ModelProfile,
) -> Array[String] {
let result : Array[String] = []
for item in self.features {
if item.locked && item.referenced {
result.push(item.name)
}
}
result
}
///|
pub fn ModelProfile::to_markdown(self : ModelProfile) -> String {
let out = StringBuilder::new()
out.write_string("# Model profile: \{self.policy_name}\n\n")
out.write_string("- Search space: \{self.search_space}\n")
out.write_string("- Mutable features: \{self.mutable_features}\n")
out.write_string("- Locked features: \{self.locked_features}\n")
out.write_string("- Expression nodes: \{self.expression_nodes}\n")
out.write_string("- Expression depth: \{self.expression_depth}\n")
out.write_string("- Comparisons: \{self.comparisons}\n")
out.write_string("- Zero-cost mutable features: \{self.zero_cost_mutable}\n")
out.write_string("- Singleton domains: \{self.singleton_domains}\n")
out.write_string(
"- Duplicate domain values: \{self.duplicate_domain_values}\n\n",
)
out.write_string(
"| feature | kind | domain | mutable values | current index | cost | status | referenced | duplicates |\n",
)
out.write_string(
"| --- | --- | ---: | ---: | ---: | ---: | --- | --- | ---: |\n",
)
for item in self.features {
out.write_string(
"| \{profile_cell(item.name)} | \{item.value_kind} | \{item.domain_size} | \{item.mutable_values} | \{item.current_index} | \{item.cost} | \{item.label()} | \{item.referenced} | \{item.duplicate_values} |\n",
)
}
out.to_string()
}
///|
pub fn ModelProfile::to_json(self : ModelProfile) -> String {
let out = StringBuilder::new()
out.write_string("{")
out.write_string("\"policy\":\"\{profile_json_escape(self.policy_name)}\",")
out.write_string("\"search_space\":\{self.search_space},")
out.write_string("\"mutable_features\":\{self.mutable_features},")
out.write_string("\"locked_features\":\{self.locked_features},")
out.write_string("\"expression_nodes\":\{self.expression_nodes},")
out.write_string("\"expression_depth\":\{self.expression_depth},")
out.write_string("\"comparisons\":\{self.comparisons},")
out.write_string("\"zero_cost_mutable\":\{self.zero_cost_mutable},")
out.write_string("\"singleton_domains\":\{self.singleton_domains},")
out.write_string(
"\"duplicate_domain_values\":\{self.duplicate_domain_values},",
)
out.write_string("\"goal_variables\":[")
for index, name in self.goal_variables {
if index > 0 {
out.write_char(',')
}
out.write_string("\"\{profile_json_escape(name)}\"")
}
out.write_string("],\"features\":[")
for index, item in self.features {
if index > 0 {
out.write_char(',')
}
out.write_string("{")
out.write_string("\"name\":\"\{profile_json_escape(item.name)}\",")
out.write_string("\"kind\":\"\{item.value_kind}\",")
out.write_string("\"domain_size\":\{item.domain_size},")
out.write_string("\"mutable_values\":\{item.mutable_values},")
out.write_string("\"current_index\":\{item.current_index},")
out.write_string("\"cost\":\{item.cost},")
out.write_string("\"locked\":\{item.locked},")
out.write_string("\"referenced\":\{item.referenced},")
out.write_string("\"duplicate_values\":\{item.duplicate_values},")
out.write_string("\"label\":\"\{item.label()}\"")
out.write_string("}")
}
out.write_string("]}")
out.to_string()
}
///|
fn profile_cell(value : String) -> String {
value.replace_all(old="|", new="\\|").replace_all(old="\n", new=" ")
}
///|
fn profile_json_escape(text : String) -> String {
let out = StringBuilder::new()
for ch in text {
match ch {
'"' => out.write_string("\\\"")
'\\' => out.write_string("\\\\")
'\n' => out.write_string("\\n")
'\r' => out.write_string("\\r")
'\t' => out.write_string("\\t")
_ => out.write_char(ch)
}
}
out.to_string()
}