use std::{any::Any, sync::Arc};
use sim_kernel::{Cx, DefaultFactory, Error, Factory, Object, Result, Symbol, Value};
use sim_lib_interference_core::{
InterferenceProblem, SamplingPlane, SamplingPolicy, SamplingThresholds, WorkBudget,
};
use sim_lib_interference_solve::ReferencePhasorSolver;
use sim_lib_numbers_tensor::active_tensor_executor;
use crate::StudyDescriptor;
pub type InterferenceStudy = StudyDescriptor;
#[derive(Clone, Copy, Debug)]
pub struct SolveRequest<'a> {
problem: &'a InterferenceProblem,
plane: &'a SamplingPlane,
sampling_policy: SamplingPolicy,
sampling_thresholds: SamplingThresholds,
work_budget: WorkBudget,
}
impl<'a> SolveRequest<'a> {
pub fn new(
problem: &'a InterferenceProblem,
plane: &'a SamplingPlane,
sampling_policy: SamplingPolicy,
sampling_thresholds: SamplingThresholds,
work_budget: WorkBudget,
) -> Self {
Self {
problem,
plane,
sampling_policy,
sampling_thresholds,
work_budget,
}
}
pub fn problem(self) -> &'a InterferenceProblem {
self.problem
}
pub fn plane(self) -> &'a SamplingPlane {
self.plane
}
pub fn sampling_policy(self) -> SamplingPolicy {
self.sampling_policy
}
pub fn sampling_thresholds(self) -> SamplingThresholds {
self.sampling_thresholds
}
pub fn work_budget(self) -> WorkBudget {
self.work_budget
}
}
pub trait StudySolver: Send + Sync + 'static {
fn solve(&self, cx: &mut Cx, request: &SolveRequest<'_>) -> Result<InterferenceStudy>;
}
#[derive(Clone)]
pub struct SolverProvider {
solver: Arc<dyn StudySolver>,
}
impl SolverProvider {
pub fn new(solver: Arc<dyn StudySolver>) -> Self {
Self { solver }
}
pub fn solver(&self) -> Arc<dyn StudySolver> {
self.solver.clone()
}
pub fn into_value(self) -> Result<Value> {
DefaultFactory.opaque(Arc::new(self))
}
}
impl Object for SolverProvider {
fn display(&self, _cx: &mut Cx) -> Result<String> {
Ok("#<interference-study-solver>".to_owned())
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl sim_kernel::ObjectCompat for SolverProvider {}
pub fn study_solver_symbol() -> Symbol {
Symbol::qualified("interference", "study-solver")
}
pub fn tensor_study_solver_symbol() -> Symbol {
Symbol::qualified("interference", "tensor-study-solver")
}
pub fn resolve_study_solver(cx: &Cx) -> Result<Arc<dyn StudySolver>> {
if let Some(value) = cx.env().get(&study_solver_symbol()) {
return solver_from_value(&value, "active environment");
}
if active_tensor_executor(cx).is_some()
&& let Some(value) = cx.registry().value_by_symbol(&tensor_study_solver_symbol())
{
return solver_from_value(value, "Tensor solver registry");
}
let value = cx
.registry()
.value_by_symbol(&study_solver_symbol())
.ok_or_else(|| Error::Eval(format!("no {} is installed", study_solver_symbol())))?;
solver_from_value(value, "runtime registry")
}
fn solver_from_value(value: &Value, source: &str) -> Result<Arc<dyn StudySolver>> {
value
.object()
.downcast_ref::<SolverProvider>()
.map(SolverProvider::solver)
.ok_or_else(|| {
Error::Eval(format!(
"{} in the {source} is not a SolverProvider",
study_solver_symbol()
))
})
}
#[derive(Clone, Copy, Debug, Default)]
pub struct ReferenceStudySolver;
impl StudySolver for ReferenceStudySolver {
fn solve(&self, _cx: &mut Cx, request: &SolveRequest<'_>) -> Result<InterferenceStudy> {
let solver = ReferencePhasorSolver::new(
request.sampling_policy(),
request.sampling_thresholds(),
request.work_budget(),
);
let (field, evidence) =
solver
.solve(request.problem(), request.plane())
.map_err(|error| {
Error::Eval(format!("interference reference solve failed: {error}"))
})?;
StudyDescriptor::from_reference(request.problem(), *request.plane(), field, &evidence)
}
}