///|
/// Search filter for experiments.
///
/// All specified conditions must match (AND semantics). An empty filter
/// matches all experiments.
pub struct ExperimentSearchFilter {
priv name_contains : String
priv description_contains : String
priv tags_any : Array[String]
} derive(Debug)
///|
/// Build an empty experiment search filter.
pub fn ExperimentSearchFilter::new() -> ExperimentSearchFilter {
{ name_contains: "", description_contains: "", tags_any: [] }
}
///|
/// Restrict to experiments whose name contains the given substring.
pub fn ExperimentSearchFilter::with_name_contains(
self : ExperimentSearchFilter,
substring : String,
) -> ExperimentSearchFilter {
{ ..self, name_contains: substring }
}
///|
/// Restrict to experiments whose description contains the given substring.
pub fn ExperimentSearchFilter::with_description_contains(
self : ExperimentSearchFilter,
substring : String,
) -> ExperimentSearchFilter {
{ ..self, description_contains: substring }
}
///|
/// Restrict to experiments that have at least one of the given tags.
pub fn ExperimentSearchFilter::with_tags_any(
self : ExperimentSearchFilter,
tags : Array[String],
) -> ExperimentSearchFilter {
{ ..self, tags_any: tags.copy() }
}
///|
/// Test whether a single experiment matches this filter.
pub fn ExperimentSearchFilter::matches(
self : ExperimentSearchFilter,
exp : Experiment,
) -> Bool {
if self.name_contains != "" {
if !exp.name().contains(self.name_contains) {
return false
}
}
if self.description_contains != "" {
if !exp.description().contains(self.description_contains) {
return false
}
}
if self.tags_any.length() > 0 {
let exp_tags = exp.tags()
let mut found = false
for t in self.tags_any {
for et in exp_tags {
if t == et {
found = true
break
}
}
if found {
break
}
}
if !found {
return false
}
}
true
}
///|
/// Search experiments in the store that match the given filter.
pub fn TrackingStore::search_experiments(
self : TrackingStore,
filter : ExperimentSearchFilter,
) -> Array[Experiment] {
let result : Array[Experiment] = []
for exp in self.experiments {
if filter.matches(exp) {
result.push(exp)
}
}
result
}
///|
/// Find experiments by tag.
pub fn TrackingStore::experiments_by_tag(
self : TrackingStore,
tag : String,
) -> Array[Experiment] {
let result : Array[Experiment] = []
for exp in self.experiments {
for t in exp.tags() {
if t == tag {
result.push(exp)
break
}
}
}
result
}
///|
/// Find experiments by name substring (case-sensitive).
pub fn TrackingStore::experiments_by_name(
self : TrackingStore,
substring : String,
) -> Array[Experiment] {
let result : Array[Experiment] = []
for exp in self.experiments {
if exp.name().contains(substring) {
result.push(exp)
}
}
result
}
///|
/// Count experiments by status of their runs.
///
/// Returns a map from run status label string to the count of runs with
/// that status across all experiments.
pub fn TrackingStore::run_status_summary(
self : TrackingStore,
) -> Map[String, Int] {
let summary : Map[String, Int] = {}
for run in self.runs {
let status_label = run.status().label()
match summary.get(status_label) {
None => summary[status_label] = 1
Some(count) => summary[status_label] = count + 1
}
}
summary
}
///|
/// Count experiments by tag.
///
/// Returns a map from tag to the count of experiments with that tag.
pub fn TrackingStore::tag_summary(self : TrackingStore) -> Map[String, Int] {
let summary : Map[String, Int] = {}
for exp in self.experiments {
for tag in exp.tags() {
match summary.get(tag) {
None => summary[tag] = 1
Some(count) => summary[tag] = count + 1
}
}
}
summary
}
///|
/// Return the total number of parameters across all runs.
pub fn TrackingStore::total_param_count(self : TrackingStore) -> Int {
let mut total = 0
for run in self.runs {
total += run.param_count()
}
total
}
///|
/// Return the total number of metrics across all runs.
pub fn TrackingStore::total_metric_count(self : TrackingStore) -> Int {
let mut total = 0
for run in self.runs {
total += run.metric_count()
}
total
}
///|
/// Return the total number of artifacts across all runs.
pub fn TrackingStore::total_artifact_count(self : TrackingStore) -> Int {
let mut total = 0
for run in self.runs {
total += run.artifact_count()
}
total
}
///|
/// Return a summary of the store's contents as a human-readable string.
pub fn TrackingStore::summary(self : TrackingStore) -> String {
let out = StringBuilder()
out <+ "MoonTrack Store Summary\n"
out <+ "=======================\n\n"
out <+ "Experiments: \{self.experiment_count()}\n"
out <+ "Runs: \{self.run_count()}\n"
out <+ "Total parameters: \{self.total_param_count()}\n"
out <+ "Total metrics: \{self.total_metric_count()}\n"
out <+ "Total artifacts: \{self.total_artifact_count()}\n\n"
out <+ "Run Status Summary:\n"
let status_summary = self.run_status_summary()
let statuses : Array[RunStatus] = [
Created,
Running,
Completed,
Failed,
Killed,
]
for status in statuses {
let count = match status_summary.get(status.label()) {
None => 0
Some(c) => c
}
out <+ " \{status.label()}: \{count}\n"
}
out <+ "\nTag Summary:\n"
let tag_summary = self.tag_summary()
let tag_keys : Array[String] = []
tag_summary.each(fn(k, _v) { tag_keys.push(k) })
for tag in tag_keys {
let count = match tag_summary.get(tag) {
None => 0
Some(c) => c
}
out <+ " \{tag}: \{count} experiments\n"
}
out.to_string()
}