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 std::ops::Index;
use crate::common::{Circuit, CircuitCalculator, DeviceKind, LcrConnError};
use crate::common::{
Circuit, CircuitCalculator, CircuitCalculatorError, DeviceKind, DeviceValueError,
sanitize_device_value,
};
use ordered_float::OrderedFloat;
use thiserror::Error as TeError;
/// The priority of the result.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResponsePriority {
/// Less devices is the first priority.
LessDevices,
@@ -15,46 +17,50 @@ pub enum ResponsePriority {
/// The maximum count for the response item count passed in request.
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.
#[derive(Clone, Debug)]
pub struct Request {
/// The kind of device to resolve.
pub device_kind: DeviceKind,
device_kind: DeviceKind,
/// The target value of the device.
pub target_value: f64,
target_value: f64,
/// The tolerance of the device in absolute value.
pub tolerance: f64,
tolerance: f64,
/// The priority principle when sorting response items.
pub response_priority: ResponsePriority,
response_priority: ResponsePriority,
/// The limited count of results.
pub count_limit: usize,
count_limit: usize,
}
impl Request {
/// 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(
device_kind: DeviceKind,
target_value: f64,
tolerance: f64,
response_priority: ResponsePriority,
count_limit: usize,
) -> Result<Self, LcrConnError> {
if target_value <= 0.0 {
return Err(LcrConnError::InvalidTargetValue(target_value));
}
if tolerance < 0.0 {
return Err(LcrConnError::InvalidTolerance(tolerance));
}
) -> Result<Self, RequestError> {
// Check arguments
let target_value =
sanitize_device_value(target_value).map_err(|err| RequestError::BadTargetValue(err))?;
let tolerance =
sanitize_device_value(tolerance).map_err(|err| RequestError::BadTolerance(err))?;
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 {
device_kind,
target_value,
@@ -63,6 +69,38 @@ impl Request {
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.
@@ -70,44 +108,34 @@ impl Request {
pub struct ResponseItem {
/// The circuit of this response item.
circuit: Circuit,
/// The device count of this circuit.
device_count: usize,
/// The value of this circuit.
value: f64,
/// The signed difference between the target value and the value of this circuit.
///
/// 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.
/// The signed difference.
difference: f64,
/// The unsigned difference between the target value and the value of this circuit.
/// The unsigned difference.
unsigned_difference: f64,
/// The signed relative difference between the target value and the value of this circuit.
///
/// 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.
/// The signed relative difference.
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,
}
impl ResponseItem {
/// Create a new response item by computing all values eagerly.
///
/// # Errors
///
/// See [`CircuitValueTrait::value`].
pub fn new(circuit: Circuit, cv_trait: &CircuitCalculator) -> Result<Self, LcrConnError> {
let value = cv_trait.value(&circuit)?;
let difference = cv_trait.difference(&circuit, Some(value))?;
let unsigned_difference = cv_trait.unsigned_difference(&circuit, None, Some(difference))?;
let relative_difference = cv_trait.relative_difference(&circuit, None, Some(difference))?;
pub fn new(circuit: Circuit, ccalc: &CircuitCalculator) -> Result<Self, ResponseError> {
// YYC MARK:
// I can use OnceLock to implement the behavior closing to Python cached_property.
// But I didn't do that due to the increased size of this struct, and inviable error handling.
// So I decide to calculate all values in there.
let value = ccalc.value(&circuit)?;
let difference = ccalc.difference(&circuit, Some(value))?;
let unsigned_difference = ccalc.unsigned_difference(&circuit, None, Some(difference))?;
let relative_difference = ccalc.relative_difference(&circuit, None, Some(difference))?;
let unsigned_relative_difference =
cv_trait.unsigned_relative_difference(&circuit, None, None, Some(relative_difference))?;
let device_count = circuit.device_scale().to_device_count();
ccalc.unsigned_relative_difference(&circuit, None, None, Some(relative_difference))?;
Ok(Self {
circuit,
device_count,
value,
difference,
unsigned_difference,
@@ -123,7 +151,7 @@ impl ResponseItem {
/// The device count of this circuit.
pub fn device_count(&self) -> usize {
self.device_count
self.circuit.device_scale().to_device_count()
}
/// The value of this circuit.
@@ -179,35 +207,29 @@ impl Response {
/// # Errors
///
/// See [`ResponseItem::new`].
pub fn new(
request: &Request,
candidates: impl IntoIterator<Item = Circuit>,
) -> Result<Self, LcrConnError> {
let cv_trait = CircuitCalculator::new(request.device_kind, request.target_value);
pub fn new<I>(request: &Request, candidates: I) -> Result<Self, ResponseError>
where
I: Iterator<Item = Circuit>,
{
let ccalc = CircuitCalculator::new(request.device_kind, request.target_value)?;
let mut items: Vec<ResponseItem> = candidates
.into_iter()
.map(|c| ResponseItem::new(c, &cv_trait))
.map(|c| ResponseItem::new(c, &ccalc))
.collect::<Result<_, _>>()?;
// Sort by different strategy
match request.response_priority {
ResponsePriority::LessDevices => {
items.sort_by(|a, b| {
a.device_count
.cmp(&b.device_count)
.then_with(|| {
a.unsigned_difference
.partial_cmp(&b.unsigned_difference)
.unwrap_or(Ordering::Equal)
})
a.device_count().cmp(&b.device_count()).then_with(|| {
OrderedFloat(a.unsigned_difference)
.cmp(&OrderedFloat(b.unsigned_difference))
})
});
}
ResponsePriority::MoreAccuracy => {
items.sort_by(|a, b| {
a.unsigned_difference
.partial_cmp(&b.unsigned_difference)
.unwrap_or(Ordering::Equal)
OrderedFloat(a.unsigned_difference).cmp(&OrderedFloat(b.unsigned_difference))
});
}
}
@@ -246,11 +268,3 @@ impl Response {
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
///
/// See [`CircuitValueTrait::value`].
pub fn new(circuit: Circuit, cv_trait: &CircuitCalculator) -> Result<Self, LcrConnError> {
let value = cv_trait.value(&circuit)?;
let unsigned_difference = cv_trait.unsigned_difference(&circuit, Some(value))?;
pub fn new(circuit: Circuit, ccalc: &CircuitCalculator) -> Result<Self, LcrConnError> {
let value = ccalc.value(&circuit)?;
let unsigned_difference = ccalc.unsigned_difference(&circuit, Some(value))?;
Ok(Self {
circuit,
value,
@@ -454,14 +454,14 @@ impl Resolver for BfsResolver {
// Iterate circuit item one by one
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)
.chain(Self::iter_two_devices_circuit(dataset))
.chain(Self::iter_three_devices_circuit(dataset));
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 item.unsigned_difference() > request.tolerance {
continue;

View File

@@ -104,7 +104,7 @@ impl Resolver for LutResolver {
let mut right = idx 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 {
if bucket.len() >= count_limit {
@@ -118,9 +118,9 @@ impl Resolver for LutResolver {
} else {
let left_item = &lut[left as usize];
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_diff = cv_trait
let right_diff = ccalc
.unsigned_difference(right_item.circuit(), Some(right_item.value()))?;
left_diff <= right_diff
};
@@ -135,7 +135,7 @@ impl Resolver for LutResolver {
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
// from target as we advance. Once one side exceeds tolerance,
// the rest of that side is guaranteed out of range — disable it.