1
0

fix: fix request error

This commit is contained in:
2026-06-29 14:35:19 +08:00
parent 24e4c595bb
commit a62ed05d50
3 changed files with 98 additions and 84 deletions

View File

@@ -1,10 +1,12 @@
use std::cmp::Ordering; use crate::common::{
use std::ops::Index; Circuit, CircuitCalculator, CircuitCalculatorError, DeviceKind, DeviceValueError,
sanitize_device_value,
use crate::common::{Circuit, CircuitCalculator, DeviceKind, LcrConnError}; };
use ordered_float::OrderedFloat;
use thiserror::Error as TeError;
/// The priority of the result. /// The priority of the result.
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResponsePriority { pub enum ResponsePriority {
/// Less devices is the first priority. /// Less devices is the first priority.
LessDevices, LessDevices,
@@ -15,46 +17,50 @@ pub enum ResponsePriority {
/// The maximum count for the response item count passed in request. /// The maximum count for the response item count passed in request.
pub const MAX_RESPONSE_CNT: usize = 50; pub const MAX_RESPONSE_CNT: usize = 50;
/// The error occurs when building [Request].
#[derive(Debug, TeError)]
pub enum RequestError {
#[error("invalid target value in request: {0}")]
BadTargetValue(DeviceValueError),
#[error("invalid tolerance in request: {0}")]
BadTolerance(DeviceValueError),
#[error("invalid response count {0} limit in request")]
BadCountLimit(usize),
}
/// All request information for the resolver. /// All request information for the resolver.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Request { pub struct Request {
/// The kind of device to resolve. /// The kind of device to resolve.
pub device_kind: DeviceKind, device_kind: DeviceKind,
/// The target value of the device. /// The target value of the device.
pub target_value: f64, target_value: f64,
/// The tolerance of the device in absolute value. /// The tolerance of the device in absolute value.
pub tolerance: f64, tolerance: f64,
/// The priority principle when sorting response items. /// The priority principle when sorting response items.
pub response_priority: ResponsePriority, response_priority: ResponsePriority,
/// The limited count of results. /// The limited count of results.
pub count_limit: usize, count_limit: usize,
} }
impl Request { impl Request {
/// Create a new request with validation. /// Create a new request with validation.
///
/// # Errors
///
/// Returns [`LcrConnError::InvalidTargetValue`] if the target value is not greater than 0.
/// Returns [`LcrConnError::InvalidTolerance`] if the tolerance is negative.
/// Returns [`LcrConnError::InvalidCountLimit`] if the count limit is 0 or exceeds
/// [`MAX_RESPONSE_CNT`].
pub fn new( pub fn new(
device_kind: DeviceKind, device_kind: DeviceKind,
target_value: f64, target_value: f64,
tolerance: f64, tolerance: f64,
response_priority: ResponsePriority, response_priority: ResponsePriority,
count_limit: usize, count_limit: usize,
) -> Result<Self, LcrConnError> { ) -> Result<Self, RequestError> {
if target_value <= 0.0 { // Check arguments
return Err(LcrConnError::InvalidTargetValue(target_value)); let target_value =
} sanitize_device_value(target_value).map_err(|err| RequestError::BadTargetValue(err))?;
if tolerance < 0.0 { let tolerance =
return Err(LcrConnError::InvalidTolerance(tolerance)); sanitize_device_value(tolerance).map_err(|err| RequestError::BadTolerance(err))?;
}
if count_limit == 0 || count_limit > MAX_RESPONSE_CNT { if count_limit == 0 || count_limit > MAX_RESPONSE_CNT {
return Err(LcrConnError::InvalidCountLimit(count_limit)); return Err(RequestError::BadCountLimit(count_limit));
} }
// Everything is okey.
Ok(Self { Ok(Self {
device_kind, device_kind,
target_value, target_value,
@@ -63,6 +69,38 @@ impl Request {
count_limit, count_limit,
}) })
} }
/// Get the kind of device of this request.
pub fn get_device_kind(&self) -> DeviceKind {
self.device_kind
}
/// Get the target value of this request.
pub fn get_target_value(&self) -> f64 {
self.target_value
}
/// Get the tolerance of this request.
pub fn get_tolerance(&self) -> f64 {
self.tolerance
}
/// Get the priority principle when sorting response items.
pub fn get_response_priority(&self) -> ResponsePriority {
self.response_priority
}
/// Get the limited count of results.
pub fn get_count_limit(&self) -> usize {
self.count_limit
}
}
/// Error occurs when building [Response] and [ResponseItem].
#[derive(Debug, TeError)]
pub enum ResponseError {
#[error("failed on computing circuit properties: {0}")]
CircuitCalculator(#[from] CircuitCalculatorError),
} }
/// The possible solution given by the resolver. /// The possible solution given by the resolver.
@@ -70,44 +108,34 @@ impl Request {
pub struct ResponseItem { pub struct ResponseItem {
/// The circuit of this response item. /// The circuit of this response item.
circuit: Circuit, circuit: Circuit,
/// The device count of this circuit.
device_count: usize,
/// The value of this circuit. /// The value of this circuit.
value: f64, value: f64,
/// The signed difference between the target value and the value of this circuit. /// The signed difference.
///
/// Positive value indicates that the value of this circuit is greater than the target value.
/// Negative value indicates that the value of this circuit is less than the target value.
difference: f64, difference: f64,
/// The unsigned difference between the target value and the value of this circuit. /// The unsigned difference.
unsigned_difference: f64, unsigned_difference: f64,
/// The signed relative difference between the target value and the value of this circuit. /// The signed relative difference.
///
/// Positive value indicates that the value of this circuit is greater than the target value.
/// Negative value indicates that the value of this circuit is less than the target value.
relative_difference: f64, relative_difference: f64,
/// The unsigned relative difference between the target value and the value of this circuit. /// The unsigned relative difference.
unsigned_relative_difference: f64, unsigned_relative_difference: f64,
} }
impl ResponseItem { impl ResponseItem {
/// Create a new response item by computing all values eagerly. /// Create a new response item by computing all values eagerly.
/// pub fn new(circuit: Circuit, ccalc: &CircuitCalculator) -> Result<Self, ResponseError> {
/// # Errors // YYC MARK:
/// // I can use OnceLock to implement the behavior closing to Python cached_property.
/// See [`CircuitValueTrait::value`]. // But I didn't do that due to the increased size of this struct, and inviable error handling.
pub fn new(circuit: Circuit, cv_trait: &CircuitCalculator) -> Result<Self, LcrConnError> { // So I decide to calculate all values in there.
let value = cv_trait.value(&circuit)?; let value = ccalc.value(&circuit)?;
let difference = cv_trait.difference(&circuit, Some(value))?; let difference = ccalc.difference(&circuit, Some(value))?;
let unsigned_difference = cv_trait.unsigned_difference(&circuit, None, Some(difference))?; let unsigned_difference = ccalc.unsigned_difference(&circuit, None, Some(difference))?;
let relative_difference = cv_trait.relative_difference(&circuit, None, Some(difference))?; let relative_difference = ccalc.relative_difference(&circuit, None, Some(difference))?;
let unsigned_relative_difference = let unsigned_relative_difference =
cv_trait.unsigned_relative_difference(&circuit, None, None, Some(relative_difference))?; ccalc.unsigned_relative_difference(&circuit, None, None, Some(relative_difference))?;
let device_count = circuit.device_scale().to_device_count();
Ok(Self { Ok(Self {
circuit, circuit,
device_count,
value, value,
difference, difference,
unsigned_difference, unsigned_difference,
@@ -123,7 +151,7 @@ impl ResponseItem {
/// The device count of this circuit. /// The device count of this circuit.
pub fn device_count(&self) -> usize { pub fn device_count(&self) -> usize {
self.device_count self.circuit.device_scale().to_device_count()
} }
/// The value of this circuit. /// The value of this circuit.
@@ -179,35 +207,29 @@ impl Response {
/// # Errors /// # Errors
/// ///
/// See [`ResponseItem::new`]. /// See [`ResponseItem::new`].
pub fn new( pub fn new<I>(request: &Request, candidates: I) -> Result<Self, ResponseError>
request: &Request, where
candidates: impl IntoIterator<Item = Circuit>, I: Iterator<Item = Circuit>,
) -> Result<Self, LcrConnError> { {
let cv_trait = CircuitCalculator::new(request.device_kind, request.target_value); let ccalc = CircuitCalculator::new(request.device_kind, request.target_value)?;
let mut items: Vec<ResponseItem> = candidates let mut items: Vec<ResponseItem> = candidates
.into_iter() .into_iter()
.map(|c| ResponseItem::new(c, &cv_trait)) .map(|c| ResponseItem::new(c, &ccalc))
.collect::<Result<_, _>>()?; .collect::<Result<_, _>>()?;
// Sort by different strategy // Sort by different strategy
match request.response_priority { match request.response_priority {
ResponsePriority::LessDevices => { ResponsePriority::LessDevices => {
items.sort_by(|a, b| { items.sort_by(|a, b| {
a.device_count a.device_count().cmp(&b.device_count()).then_with(|| {
.cmp(&b.device_count) OrderedFloat(a.unsigned_difference)
.then_with(|| { .cmp(&OrderedFloat(b.unsigned_difference))
a.unsigned_difference
.partial_cmp(&b.unsigned_difference)
.unwrap_or(Ordering::Equal)
}) })
}); });
} }
ResponsePriority::MoreAccuracy => { ResponsePriority::MoreAccuracy => {
items.sort_by(|a, b| { items.sort_by(|a, b| {
a.unsigned_difference OrderedFloat(a.unsigned_difference).cmp(&OrderedFloat(b.unsigned_difference))
.partial_cmp(&b.unsigned_difference)
.unwrap_or(Ordering::Equal)
}); });
} }
} }
@@ -246,11 +268,3 @@ impl Response {
self.sorted_items.iter() self.sorted_items.iter()
} }
} }
impl Index<usize> for Response {
type Output = ResponseItem;
fn index(&self, index: usize) -> &Self::Output {
&self.sorted_items[index]
}
}

View File

@@ -263,9 +263,9 @@ impl BfsItem {
/// # Errors /// # Errors
/// ///
/// See [`CircuitValueTrait::value`]. /// See [`CircuitValueTrait::value`].
pub fn new(circuit: Circuit, cv_trait: &CircuitCalculator) -> Result<Self, LcrConnError> { pub fn new(circuit: Circuit, ccalc: &CircuitCalculator) -> Result<Self, LcrConnError> {
let value = cv_trait.value(&circuit)?; let value = ccalc.value(&circuit)?;
let unsigned_difference = cv_trait.unsigned_difference(&circuit, Some(value))?; let unsigned_difference = ccalc.unsigned_difference(&circuit, Some(value))?;
Ok(Self { Ok(Self {
circuit, circuit,
value, value,
@@ -454,14 +454,14 @@ impl Resolver for BfsResolver {
// Iterate circuit item one by one // Iterate circuit item one by one
let mut bucket = ResultBucket::new(request.count_limit); let mut bucket = ResultBucket::new(request.count_limit);
let cv_trait = CircuitCalculator::new(request.device_kind, request.target_value); let ccalc = CircuitCalculator::new(request.device_kind, request.target_value);
let circuits = Self::iter_one_device_circuit(dataset) let circuits = Self::iter_one_device_circuit(dataset)
.chain(Self::iter_two_devices_circuit(dataset)) .chain(Self::iter_two_devices_circuit(dataset))
.chain(Self::iter_three_devices_circuit(dataset)); .chain(Self::iter_three_devices_circuit(dataset));
for circuit in circuits { for circuit in circuits {
let item = BfsItem::new(circuit, &cv_trait)?; let item = BfsItem::new(circuit, &ccalc)?;
// If circuit absolute difference is out of tolerance, skip it directly. // If circuit absolute difference is out of tolerance, skip it directly.
if item.unsigned_difference() > request.tolerance { if item.unsigned_difference() > request.tolerance {
continue; continue;

View File

@@ -104,7 +104,7 @@ impl Resolver for LutResolver {
let mut right = idx as isize; let mut right = idx as isize;
let lut_len = lut.len() as isize; let lut_len = lut.len() as isize;
let cv_trait = CircuitCalculator::new(request.device_kind, target); let ccalc = CircuitCalculator::new(request.device_kind, target);
while left >= 0 || right < lut_len { while left >= 0 || right < lut_len {
if bucket.len() >= count_limit { if bucket.len() >= count_limit {
@@ -118,9 +118,9 @@ impl Resolver for LutResolver {
} else { } else {
let left_item = &lut[left as usize]; let left_item = &lut[left as usize];
let left_diff = let left_diff =
cv_trait.unsigned_difference(left_item.circuit(), Some(left_item.value()))?; ccalc.unsigned_difference(left_item.circuit(), Some(left_item.value()))?;
let right_item = &lut[right as usize]; let right_item = &lut[right as usize];
let right_diff = cv_trait let right_diff = ccalc
.unsigned_difference(right_item.circuit(), Some(right_item.value()))?; .unsigned_difference(right_item.circuit(), Some(right_item.value()))?;
left_diff <= right_diff left_diff <= right_diff
}; };
@@ -135,7 +135,7 @@ impl Resolver for LutResolver {
item item
}; };
let diff = cv_trait.unsigned_difference(item.circuit(), Some(item.value()))?; let diff = ccalc.unsigned_difference(item.circuit(), Some(item.value()))?;
// Since the LUT is sorted, values on each side only move further // Since the LUT is sorted, values on each side only move further
// from target as we advance. Once one side exceeds tolerance, // from target as we advance. Once one side exceeds tolerance,
// the rest of that side is guaranteed out of range — disable it. // the rest of that side is guaranteed out of range — disable it.