From a855fa226016ad9c329c7faead3a0c8de3eb36f3 Mon Sep 17 00:00:00 2001 From: pesap Date: Wed, 13 May 2026 18:06:54 -0600 Subject: [PATCH] chore(arco-ops): remove stale time-set metadata --- crates/arco-ops/src/benchmark.rs | 21 ++-- .../compile/compile/constraints_bindings.rs | 22 +--- .../compile/compile/expressions_domains.rs | 6 +- .../arco-ops/src/compile/compile/nonlinear.rs | 4 +- crates/arco-ops/src/compile/semantic/types.rs | 112 ++++-------------- .../src/compile/semantic/validation.rs | 15 +-- 6 files changed, 39 insertions(+), 141 deletions(-) diff --git a/crates/arco-ops/src/benchmark.rs b/crates/arco-ops/src/benchmark.rs index cb7eb2f9..8a04b981 100644 --- a/crates/arco-ops/src/benchmark.rs +++ b/crates/arco-ops/src/benchmark.rs @@ -75,7 +75,6 @@ pub struct ExpectedSets { #[derive(Debug, Clone, PartialEq, Eq, Deserialize)] pub struct ExpectedTimeSet { pub steps: usize, - pub resolution: String, } pub type ExpectedParameters = ResolvedParameters; @@ -178,8 +177,7 @@ fn to_semantic_expectation( .map(|set| set.values.clone()) .unwrap_or_default(), time: ExpectedTimeSet { - steps: program.sets.time.steps, - resolution: program.sets.time.resolution.to_string(), + steps: program.time_steps(), }, }, parameters: ExpectedParameters { @@ -251,8 +249,7 @@ fn read_json Deserialize<'de>>(path: &Path) -> Result SemanticProgram { SemanticProgram { active_scenario: "Base".to_string(), - sets: ResolvedSets { - time: ResolvedTimeSet { - steps: 24, - resolution: TimeResolution::Hourly, + set_registry: BTreeMap::from([( + "time".to_string(), + ResolvedSet { + values: (1..=24).map(|value| value.to_string()).collect(), + tuple_components: None, + tuple_component_domains: None, + tuple_rows: None, }, - }, - set_registry: BTreeMap::new(), + )]), set_aliases: BTreeMap::new(), set_params: BTreeMap::new(), parameters: ResolvedParameters::default(), diff --git a/crates/arco-ops/src/compile/compile/constraints_bindings.rs b/crates/arco-ops/src/compile/compile/constraints_bindings.rs index 49bbb29c..d6e8530b 100644 --- a/crates/arco-ops/src/compile/compile/constraints_bindings.rs +++ b/crates/arco-ops/src/compile/compile/constraints_bindings.rs @@ -27,7 +27,7 @@ fn emit_terminal_boundary_constraints( terms: vec![term( &format!( "{}[{},{}]", - soc_signature.target, asset.name, program.sets.time.steps + soc_signature.target, asset.name, program.time_steps() ), 1.0, )], @@ -99,25 +99,7 @@ fn infer_constraint_generation_bindings( } fn domain_is_time_like(program: &SemanticProgram, domain: &str) -> bool { - if domain == "time" || domain == "t" { - return true; - } - let set = program.set_registry.get(domain).or_else(|| { - program - .set_aliases - .get(domain) - .and_then(|canonical| program.set_registry.get(canonical)) - }); - let Some(set) = set else { - return false; - }; - if set.values.len() != program.sets.time.steps || set.values.is_empty() { - return false; - } - set.values - .iter() - .enumerate() - .all(|(offset, value)| value.parse::().ok() == Some(offset + 1)) + program.is_time_set_name(domain) } fn infer_constraint_binding_domains_from_body( diff --git a/crates/arco-ops/src/compile/compile/expressions_domains.rs b/crates/arco-ops/src/compile/compile/expressions_domains.rs index 57f7db94..afb36635 100644 --- a/crates/arco-ops/src/compile/compile/expressions_domains.rs +++ b/crates/arco-ops/src/compile/compile/expressions_domains.rs @@ -138,7 +138,7 @@ fn reduction_domain_values( .filter(|asset| asset.candidate) .map(|asset| FilterValue::String(asset.name.clone())) .collect()), - "time" => Ok((1..=program.sets.time.steps) + domain if program.is_time_set_name(domain) => Ok((1..=program.time_steps()) .map(|time| FilterValue::Number(time as f64)) .collect()), _ => { @@ -441,7 +441,7 @@ fn linearize_indexed_expr( // normal 1..=steps range AND a variable family with matching // arity exists (so we know this target is a variable, not a // parameter that happens to be missing). - if !(1..=program.sets.time.steps as i64).contains(&time) + if !(1..=program.time_steps() as i64).contains(&time) && find_variable_family(target, resolved.len(), variable_signatures).is_some() { if let Some(value) = @@ -458,7 +458,7 @@ fn linearize_indexed_expr( if let [FilterValue::Number(_)] = resolved.as_slice() { let time = integer_time_index(&resolved[0], entrypoint)?; - if !(1..=program.sets.time.steps as i64).contains(&time) + if !(1..=program.time_steps() as i64).contains(&time) && find_variable_family(target, resolved.len(), variable_signatures).is_some() { return Err(CompileError::InvalidFormulation { diff --git a/crates/arco-ops/src/compile/compile/nonlinear.rs b/crates/arco-ops/src/compile/compile/nonlinear.rs index 64b49a01..c808f0bd 100644 --- a/crates/arco-ops/src/compile/compile/nonlinear.rs +++ b/crates/arco-ops/src/compile/compile/nonlinear.rs @@ -643,7 +643,7 @@ fn compile_nonlinear_indexed_expr( let asset_name = string_filter_value(&resolved[0], &synthetic, entrypoint)?; let time = integer_time_index(&resolved[1], entrypoint)?; - if !(1..=program.sets.time.steps as i64).contains(&time) + if !(1..=program.time_steps() as i64).contains(&time) && find_variable_family(target, resolved.len(), variable_signatures).is_some() { if let Some(value) = @@ -660,7 +660,7 @@ fn compile_nonlinear_indexed_expr( if let [FilterValue::Number(_)] = resolved.as_slice() { let time = integer_time_index(&resolved[0], entrypoint)?; - if !(1..=program.sets.time.steps as i64).contains(&time) + if !(1..=program.time_steps() as i64).contains(&time) && find_variable_family(target, resolved.len(), variable_signatures).is_some() { return Err(CompileError::InvalidFormulation { diff --git a/crates/arco-ops/src/compile/semantic/types.rs b/crates/arco-ops/src/compile/semantic/types.rs index d0ac01af..7ac15dea 100644 --- a/crates/arco-ops/src/compile/semantic/types.rs +++ b/crates/arco-ops/src/compile/semantic/types.rs @@ -3,88 +3,6 @@ use arco_kdl::algebra::{ConstraintBody, Expr}; use arco_kdl::source::{BoundExpr, GenerationBinding, VariableKindDecl}; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; -use std::fmt; -use std::str::FromStr; - -/// Time resolution for the time horizon, using ISO 8601 duration format. -#[derive( - Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, -)] -pub enum TimeResolution { - /// 15 minutes (PT15M) - FifteenMinutes, - /// 30 minutes (PT30M) - ThirtyMinutes, - /// 1 hour (PT1H) - #[default] - Hourly, - /// 1 day (P1D) - Daily, - /// 1 week (P1W) - Weekly, - /// 1 month (P1M) - Monthly, - /// 1 year (P1Y) - Yearly, -} - -impl TimeResolution { - /// Returns the duration in hours for this resolution. - pub fn as_hours(&self) -> f64 { - match self { - TimeResolution::FifteenMinutes => 0.25, - TimeResolution::ThirtyMinutes => 0.5, - TimeResolution::Hourly => 1.0, - TimeResolution::Daily => 24.0, - TimeResolution::Weekly => 168.0, // 24 * 7 - TimeResolution::Monthly => 720.0, // Approximate: 30 days - TimeResolution::Yearly => 8760.0, // Approximate: 365 days - } - } - - /// Returns the ISO 8601 duration string representation. - pub fn as_iso8601(&self) -> &'static str { - match self { - TimeResolution::FifteenMinutes => "PT15M", - TimeResolution::ThirtyMinutes => "PT30M", - TimeResolution::Hourly => "PT1H", - TimeResolution::Daily => "P1D", - TimeResolution::Weekly => "P1W", - TimeResolution::Monthly => "P1M", - TimeResolution::Yearly => "P1Y", - } - } -} - -impl fmt::Display for TimeResolution { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.as_iso8601()) - } -} - -impl FromStr for TimeResolution { - type Err = String; - - fn from_str(s: &str) -> Result { - match s { - "PT15M" | "PT15m" | "15min" | "15MIN" => Ok(TimeResolution::FifteenMinutes), - "PT30M" | "PT30m" | "30min" | "30MIN" => Ok(TimeResolution::ThirtyMinutes), - "PT1H" | "PT1h" | "1h" | "1H" | "hourly" | "HOURLY" | "Hourly" => { - Ok(TimeResolution::Hourly) - } - "P1D" | "P1d" | "1d" | "1D" | "daily" | "DAILY" | "Daily" => Ok(TimeResolution::Daily), - "P1W" | "P1w" | "1w" | "1W" | "weekly" | "WEEKLY" | "Weekly" => { - Ok(TimeResolution::Weekly) - } - "P1M" | "P1m" | "1m" | "1M" | "monthly" | "MONTHLY" | "Monthly" => { - Ok(TimeResolution::Monthly) - } - "P1Y" | "P1y" | "1y" | "1Y" | "yearly" | "YEARLY" | "Yearly" | "annual" | "ANNUAL" - | "Annual" => Ok(TimeResolution::Yearly), - _ => Err(format!("Unknown time resolution: {}", s)), - } - } -} #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct FamilySignature { @@ -144,7 +62,6 @@ pub struct VariableDeclOverrides { #[derive(Debug, Clone, PartialEq)] pub struct SemanticProgram { pub active_scenario: String, - pub sets: ResolvedSets, pub set_registry: BTreeMap, pub set_aliases: BTreeMap, pub set_params: BTreeMap>, @@ -160,15 +77,28 @@ pub struct SemanticProgram { pub active_dual_reports: Vec, } -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ResolvedSets { - pub time: ResolvedTimeSet, -} +impl SemanticProgram { + pub fn time_steps(&self) -> usize { + self.time_set().map_or(0, |set| set.values.len()) + } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct ResolvedTimeSet { - pub steps: usize, - pub resolution: TimeResolution, + pub fn is_time_set_name(&self, name: &str) -> bool { + self.resolve_set(name) + .zip(self.time_set()) + .is_some_and(|(candidate, time_set)| std::ptr::eq(candidate, time_set)) + } + + fn time_set(&self) -> Option<&ResolvedSet> { + self.resolve_set("time").or_else(|| self.resolve_set("t")) + } + + fn resolve_set(&self, name: &str) -> Option<&ResolvedSet> { + self.set_registry.get(name).or_else(|| { + self.set_aliases + .get(name) + .and_then(|canonical| self.set_registry.get(canonical)) + }) + } } #[derive(Debug, Clone, PartialEq)] diff --git a/crates/arco-ops/src/compile/semantic/validation.rs b/crates/arco-ops/src/compile/semantic/validation.rs index 9222e96f..206c3b14 100644 --- a/crates/arco-ops/src/compile/semantic/validation.rs +++ b/crates/arco-ops/src/compile/semantic/validation.rs @@ -7,8 +7,7 @@ use crate::compile::semantic::sets::{ }; use crate::compile::semantic::types::{ FamilySignature, ResolvedChronology, ResolvedConstraint, ResolvedExpression, ResolvedObjective, - ResolvedParameters, ResolvedReport, ResolvedSets, ResolvedTimeSet, SemanticProgram, - TimeResolution, VariableDeclOverrides, + ResolvedParameters, ResolvedReport, SemanticProgram, VariableDeclOverrides, }; use arco_kdl::algebra::parse_value_formula; use arco_kdl::source::{BoundExpr, ModelDecl, ScenarioDecl, SourceProgram, VariableKindDecl}; @@ -179,20 +178,8 @@ pub fn validate_program( } } - let time_steps = set_registry - .get("time") - .or_else(|| set_registry.get("t")) - .map_or(0, |set| set.values.len()); - let resolved_sets = ResolvedSets { - time: ResolvedTimeSet { - steps: time_steps, - resolution: TimeResolution::default(), - }, - }; - Ok(SemanticProgram { active_scenario: scenario.name.clone(), - sets: resolved_sets, set_registry, set_aliases: set_aliases.clone(), set_params: BTreeMap::new(),