refactor: continue refactoring kernel
This commit is contained in:
+26
-38
@@ -1,12 +1,11 @@
|
|||||||
use crate::common::{
|
use crate::common::{
|
||||||
Circuit, CircuitCalculator, CircuitCalculatorError, DeviceKind, DeviceValueError,
|
Circuit, CircuitError, CircuitEvaluation, DeviceKind, DeviceValueError, validate_device_value,
|
||||||
validate_device_value,
|
|
||||||
};
|
};
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use thiserror::Error as TeError;
|
use thiserror::Error as TeError;
|
||||||
|
|
||||||
/// The priority of the result.
|
/// The priority of the result.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum ResponsePriority {
|
pub enum ResponsePriority {
|
||||||
/// Less devices is the first priority.
|
/// Less devices is the first priority.
|
||||||
LessDevices,
|
LessDevices,
|
||||||
@@ -76,11 +75,15 @@ impl Request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the target value of this request.
|
/// Get the target value of this request.
|
||||||
|
///
|
||||||
|
/// The return value was ensured that it must be valid device value.
|
||||||
pub fn get_target_value(&self) -> f64 {
|
pub fn get_target_value(&self) -> f64 {
|
||||||
self.target_value
|
self.target_value
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the tolerance of this request.
|
/// Get the tolerance of this request.
|
||||||
|
///
|
||||||
|
/// The return value was ensured that it must be unsigned non-relative valid device value.
|
||||||
pub fn get_tolerance(&self) -> f64 {
|
pub fn get_tolerance(&self) -> f64 {
|
||||||
self.tolerance
|
self.tolerance
|
||||||
}
|
}
|
||||||
@@ -91,6 +94,8 @@ impl Request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the limited count of results.
|
/// Get the limited count of results.
|
||||||
|
///
|
||||||
|
/// The return value was ensured that it must >= 0 and < [`MAX_RESPONSE_CNT`].
|
||||||
pub fn get_count_limit(&self) -> usize {
|
pub fn get_count_limit(&self) -> usize {
|
||||||
self.count_limit
|
self.count_limit
|
||||||
}
|
}
|
||||||
@@ -99,8 +104,8 @@ impl Request {
|
|||||||
/// Error occurs when building [Response] and [ResponseItem].
|
/// Error occurs when building [Response] and [ResponseItem].
|
||||||
#[derive(Debug, TeError)]
|
#[derive(Debug, TeError)]
|
||||||
pub enum ResponseError {
|
pub enum ResponseError {
|
||||||
#[error("failed on computing circuit properties: {0}")]
|
#[error("failed on evaluating circuit: {0}")]
|
||||||
CircuitCalculator(#[from] CircuitCalculatorError),
|
EvaluateCircuit(#[from] CircuitError),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The possible solution given by the resolver.
|
/// The possible solution given by the resolver.
|
||||||
@@ -108,39 +113,23 @@ pub enum ResponseError {
|
|||||||
pub struct ResponseItem {
|
pub struct ResponseItem {
|
||||||
/// The circuit of this response item.
|
/// The circuit of this response item.
|
||||||
circuit: Circuit,
|
circuit: Circuit,
|
||||||
/// The value of this circuit.
|
/// The evaluation result of this circuit.
|
||||||
value: f64,
|
circuit_evaluation: CircuitEvaluation,
|
||||||
/// The signed difference.
|
|
||||||
difference: f64,
|
|
||||||
/// The unsigned difference.
|
|
||||||
unsigned_difference: f64,
|
|
||||||
/// The signed relative difference.
|
|
||||||
relative_difference: f64,
|
|
||||||
/// The unsigned relative difference.
|
|
||||||
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> {
|
fn new(circuit: Circuit, request: &Request) -> Result<Self, ResponseError> {
|
||||||
// YYC MARK:
|
// YYC MARK:
|
||||||
// I can use OnceLock to implement the behavior closing to Python cached_property.
|
// 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.
|
// 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.
|
// So I decide to calculate all values in there.
|
||||||
let value = ccalc.value(&circuit)?;
|
let circuit_evaluation =
|
||||||
let difference = ccalc.difference(&circuit, Some(value))?;
|
circuit.evaluate_with_target(request.get_target_value(), request.get_device_kind())?;
|
||||||
let unsigned_difference = ccalc.unsigned_difference(&circuit, None, Some(difference))?;
|
// Build self and return
|
||||||
let relative_difference = ccalc.relative_difference(&circuit, None, Some(difference))?;
|
|
||||||
let unsigned_relative_difference =
|
|
||||||
ccalc.unsigned_relative_difference(&circuit, None, None, Some(relative_difference))?;
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
circuit,
|
circuit,
|
||||||
value,
|
circuit_evaluation,
|
||||||
difference,
|
|
||||||
unsigned_difference,
|
|
||||||
relative_difference,
|
|
||||||
unsigned_relative_difference,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,7 +145,7 @@ impl ResponseItem {
|
|||||||
|
|
||||||
/// The value of this circuit.
|
/// The value of this circuit.
|
||||||
pub fn value(&self) -> f64 {
|
pub fn value(&self) -> f64 {
|
||||||
self.value
|
self.circuit_evaluation.value
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The signed difference between the target value and the value of this circuit.
|
/// The signed difference between the target value and the value of this circuit.
|
||||||
@@ -164,12 +153,12 @@ impl ResponseItem {
|
|||||||
/// Positive value indicates that the value of this circuit is greater than the target value.
|
/// 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.
|
/// Negative value indicates that the value of this circuit is less than the target value.
|
||||||
pub fn difference(&self) -> f64 {
|
pub fn difference(&self) -> f64 {
|
||||||
self.difference
|
self.circuit_evaluation.difference
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The unsigned difference between the target value and the value of this circuit.
|
/// The unsigned difference between the target value and the value of this circuit.
|
||||||
pub fn unsigned_difference(&self) -> f64 {
|
pub fn unsigned_difference(&self) -> f64 {
|
||||||
self.unsigned_difference
|
self.circuit_evaluation.unsigned_difference
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The signed relative difference between the target value and the value of this circuit.
|
/// The signed relative difference between the target value and the value of this circuit.
|
||||||
@@ -177,12 +166,12 @@ impl ResponseItem {
|
|||||||
/// Positive value indicates that the value of this circuit is greater than the target value.
|
/// 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.
|
/// Negative value indicates that the value of this circuit is less than the target value.
|
||||||
pub fn relative_difference(&self) -> f64 {
|
pub fn relative_difference(&self) -> f64 {
|
||||||
self.relative_difference
|
self.circuit_evaluation.relative_difference
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The unsigned relative difference between the target value and the value of this circuit.
|
/// The unsigned relative difference between the target value and the value of this circuit.
|
||||||
pub fn unsigned_relative_difference(&self) -> f64 {
|
pub fn unsigned_relative_difference(&self) -> f64 {
|
||||||
self.unsigned_relative_difference
|
self.circuit_evaluation.unsigned_relative_difference
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,10 +200,9 @@ impl Response {
|
|||||||
where
|
where
|
||||||
I: Iterator<Item = Circuit>,
|
I: Iterator<Item = Circuit>,
|
||||||
{
|
{
|
||||||
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, &ccalc))
|
.map(|c| ResponseItem::new(c, request))
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
// Sort by different strategy
|
// Sort by different strategy
|
||||||
@@ -222,14 +210,14 @@ impl Response {
|
|||||||
ResponsePriority::LessDevices => {
|
ResponsePriority::LessDevices => {
|
||||||
items.sort_by(|a, b| {
|
items.sort_by(|a, b| {
|
||||||
a.device_count().cmp(&b.device_count()).then_with(|| {
|
a.device_count().cmp(&b.device_count()).then_with(|| {
|
||||||
OrderedFloat(a.unsigned_difference)
|
OrderedFloat(a.unsigned_difference())
|
||||||
.cmp(&OrderedFloat(b.unsigned_difference))
|
.cmp(&OrderedFloat(b.unsigned_difference()))
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ResponsePriority::MoreAccuracy => {
|
ResponsePriority::MoreAccuracy => {
|
||||||
items.sort_by(|a, b| {
|
items.sort_by(|a, b| {
|
||||||
OrderedFloat(a.unsigned_difference).cmp(&OrderedFloat(b.unsigned_difference))
|
OrderedFloat(a.unsigned_difference()).cmp(&OrderedFloat(b.unsigned_difference()))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+171
-144
@@ -1,9 +1,7 @@
|
|||||||
use super::{Resolver, ResolverError};
|
use super::{Resolver, ResolverError};
|
||||||
use crate::common::{
|
use crate::common::{Circuit, CircuitError, DeviceKind, JointKind};
|
||||||
Circuit, CircuitCalculator, CircuitCalculatorError, CircuitError, DeviceKind, JointKind,
|
|
||||||
};
|
|
||||||
use crate::spec::{SpecGroup, SpecCatalog};
|
|
||||||
use crate::query::{Request, Response, ResponseError};
|
use crate::query::{Request, Response, ResponseError};
|
||||||
|
use crate::spec::{SpecCatalog, SpecGroup};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
@@ -11,15 +9,13 @@ use std::collections::BinaryHeap;
|
|||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
use thiserror::Error as TeError;
|
use thiserror::Error as TeError;
|
||||||
|
|
||||||
|
// region: BFS Resolver Kernel
|
||||||
|
|
||||||
/// Error occurs BFS resolver.
|
/// Error occurs BFS resolver.
|
||||||
#[derive(Debug, TeError)]
|
#[derive(Debug, TeError)]
|
||||||
pub enum BfsResolverError {
|
pub enum BfsResolverError {
|
||||||
#[error("failed to build circuit: {0}")]
|
#[error("failed on evaluating circuit: {0}")]
|
||||||
Circuit(#[from] CircuitError),
|
EvaluateCircuit(#[from] CircuitError),
|
||||||
#[error("failed on computing circuit properties: {0}")]
|
|
||||||
CircuitCalculator(#[from] CircuitCalculatorError),
|
|
||||||
#[error("the size of binary heap {0} is invalid")]
|
|
||||||
BadBinHeapSize(usize),
|
|
||||||
#[error("fail to build response: {0}")]
|
#[error("fail to build response: {0}")]
|
||||||
Response(#[from] ResponseError),
|
Response(#[from] ResponseError),
|
||||||
}
|
}
|
||||||
@@ -38,15 +34,16 @@ pub struct BfsItem {
|
|||||||
|
|
||||||
impl BfsItem {
|
impl BfsItem {
|
||||||
/// Create a new BFS item by computing values eagerly.
|
/// Create a new BFS item by computing values eagerly.
|
||||||
pub fn new(circuit: Circuit, ccalc: &CircuitCalculator) -> Result<Self, BfsResolverError> {
|
pub fn new(circuit: Circuit, request: &Request) -> Result<Self, BfsResolverError> {
|
||||||
// YYC MARK:
|
// YYC MARK:
|
||||||
// The same reason for replacing cached_property like I done in `ResponseItem`.
|
// The same reason for replacing cached_property like I done in `ResponseItem`.
|
||||||
let value = ccalc.value(&circuit)?;
|
let eval =
|
||||||
let unsigned_difference = ccalc.unsigned_difference(&circuit, Some(value), None)?;
|
circuit.evaluate_with_target(request.get_target_value(), request.get_device_kind())?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
circuit,
|
circuit,
|
||||||
value,
|
value: eval.value,
|
||||||
unsigned_difference,
|
unsigned_difference: eval.unsigned_difference,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +70,153 @@ impl BfsItem {
|
|||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Result Bucket
|
// region: BFS Resolver
|
||||||
|
|
||||||
|
/// A resolver that uses breadth first search to find the best matching circuits.
|
||||||
|
pub struct BfsResolver {
|
||||||
|
/// The datasets for all device kinds.
|
||||||
|
datasets: SpecCatalog,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BfsResolver {
|
||||||
|
// YYC MARK:
|
||||||
|
// Some circuit are equivalent in topology.
|
||||||
|
// If we deduplicate these equaivalent circuit in building result, there are too complex works.
|
||||||
|
// So we should deduplicated these equivalent circuit at the beginning, i.e. when generating them.
|
||||||
|
// So following iterator functions are taking this job.
|
||||||
|
//
|
||||||
|
// Additionally, these device values are coming from `spec`.
|
||||||
|
// All values are verified so the building step must success.
|
||||||
|
// So we can safely unwrap them.
|
||||||
|
|
||||||
|
/// Iterate all possible circuits with one device without repeating equivalent topology.
|
||||||
|
pub fn iter_one_device_circuit(specs: &SpecGroup) -> impl Iterator<Item = Circuit> {
|
||||||
|
// Every single device is unique so we directly output them.
|
||||||
|
// This feature is insured by dataset itself.
|
||||||
|
specs
|
||||||
|
.iter()
|
||||||
|
.map(|v1| Circuit::from_one_device(v1).expect("unexpected failure on building circuit"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate all possible circuits with two devices without repeating equivalent topology.
|
||||||
|
pub fn iter_two_devices_circuit(specs: &SpecGroup) -> impl Iterator<Item = Circuit> {
|
||||||
|
// The two devices in this circuit is always swapable,
|
||||||
|
// so we iterate them without repeating.
|
||||||
|
itertools::iproduct!(
|
||||||
|
specs.iter().array_combinations_with_replacement::<2>(),
|
||||||
|
JointKind::iter()
|
||||||
|
)
|
||||||
|
.map(|([v1, v2], j2)| {
|
||||||
|
Circuit::from_two_devices(v1, v2, j2).expect("unexpected failure on building circuit")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate all possible circuits with three devices without repeating equivalent topology.
|
||||||
|
pub fn iter_three_devices_circuit(specs: &SpecGroup) -> impl Iterator<Item = Circuit> {
|
||||||
|
// For generating three devices circuit,
|
||||||
|
// it should be consisted by 2 parts.
|
||||||
|
itertools::chain!(
|
||||||
|
// First, the whole circuit has only one joint type.
|
||||||
|
// In this case, 3 devices are swapable and we should iterate them without repeating
|
||||||
|
itertools::iproduct!(
|
||||||
|
specs.iter().array_combinations_with_replacement::<3>(),
|
||||||
|
JointKind::iter()
|
||||||
|
)
|
||||||
|
.map(
|
||||||
|
|([v1, v2, v3], j)| Circuit::from_three_devices(v1, v2, j, v3, j)
|
||||||
|
.expect("unexpected failure on building circuit")
|
||||||
|
),
|
||||||
|
// Second, if the joint type is different, then the first 2 devices are swapable.
|
||||||
|
// So we need iterate them without repeating.
|
||||||
|
itertools::iproduct!(
|
||||||
|
specs.iter().array_combinations_with_replacement::<2>(),
|
||||||
|
specs.iter(),
|
||||||
|
JointKind::iter()
|
||||||
|
)
|
||||||
|
.map(|([v1, v2], v3, j)| Circuit::from_three_devices(
|
||||||
|
v1,
|
||||||
|
v2,
|
||||||
|
j,
|
||||||
|
v3,
|
||||||
|
j.flip()
|
||||||
|
)
|
||||||
|
.expect("unexpected failure on building circuit")),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BfsResolver {
|
||||||
|
/// Create a new BFS resolver with the given datasets.
|
||||||
|
pub fn new(datasets: SpecCatalog) -> Self {
|
||||||
|
Self { datasets }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pick_specs(&self, device_kind: DeviceKind) -> &SpecGroup {
|
||||||
|
match device_kind {
|
||||||
|
DeviceKind::Resistor => self.datasets.resistor_specs(),
|
||||||
|
DeviceKind::Capacitor => self.datasets.capacitor_specs(),
|
||||||
|
DeviceKind::Inductor => self.datasets.inductor_specs(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bfs_iteration(
|
||||||
|
specs: &SpecGroup,
|
||||||
|
request: &Request,
|
||||||
|
) -> impl Iterator<Item = Result<BfsItem, BfsResolverError>> {
|
||||||
|
itertools::chain!(
|
||||||
|
BfsResolver::iter_one_device_circuit(&specs),
|
||||||
|
BfsResolver::iter_two_devices_circuit(&specs),
|
||||||
|
BfsResolver::iter_three_devices_circuit(&specs)
|
||||||
|
)
|
||||||
|
.map(|circuit| BfsItem::new(circuit, request))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn intern_resolve(&self, request: &Request) -> Result<Response, BfsResolverError> {
|
||||||
|
// Pick dataset from collection
|
||||||
|
let specs = self.pick_specs(request.get_device_kind());
|
||||||
|
|
||||||
|
// Create the result bucket.
|
||||||
|
// The count limit held by request is must be greater than zero, so we can simply unwrap it.
|
||||||
|
let mut bucket =
|
||||||
|
ResultBucket::new(request.get_count_limit()).expect("unexpected blank result bucket");
|
||||||
|
|
||||||
|
// Iterate circuit item one by one
|
||||||
|
for item in BfsResolver::bfs_iteration(specs, request) {
|
||||||
|
let item = item?;
|
||||||
|
// If circuit absolute difference is out of tolerance, skip it directly.
|
||||||
|
if item.unsigned_difference() <= request.get_tolerance() {
|
||||||
|
// Put it into bucket
|
||||||
|
let score = item.unsigned_difference();
|
||||||
|
bucket.insert(item, score);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return result
|
||||||
|
let circuits = bucket.into_iter().map(|i| i.into_circuit());
|
||||||
|
Ok(Response::new(request, circuits)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resolver for BfsResolver {
|
||||||
|
fn resolve(&self, request: &Request) -> Result<Response, ResolverError> {
|
||||||
|
Ok(self.intern_resolve(request)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion:
|
||||||
|
|
||||||
|
// region: Result Bucket Helper
|
||||||
|
|
||||||
|
/// The error occurs in [`ResultBucket`] and [`ResultBucketItem`].
|
||||||
|
#[derive(Debug, TeError)]
|
||||||
|
enum ResultBucketError {
|
||||||
|
#[error("the size of binary heap {0} is invalid")]
|
||||||
|
BadBinHeapSize(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
// region: Result Bucket Item
|
||||||
|
|
||||||
/// An item stored in a [`ResultBucket`].
|
/// An item stored in a [`ResultBucket`].
|
||||||
struct ResultBucketItem {
|
struct ResultBucketItem {
|
||||||
@@ -128,6 +271,10 @@ impl Ord for ResultBucketItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region: Result Bucket
|
||||||
|
|
||||||
/// A bounded bucket that keeps up to N entries with the smallest scores.
|
/// A bounded bucket that keeps up to N entries with the smallest scores.
|
||||||
///
|
///
|
||||||
/// When the bucket is full, inserting a new item only succeeds if its score
|
/// When the bucket is full, inserting a new item only succeeds if its score
|
||||||
@@ -145,10 +292,10 @@ pub struct ResultBucket {
|
|||||||
|
|
||||||
impl ResultBucket {
|
impl ResultBucket {
|
||||||
/// Create a new bucket that holds at most `n` items.
|
/// Create a new bucket that holds at most `n` items.
|
||||||
pub fn new(n: usize) -> Result<Self, BfsResolverError> {
|
pub fn new(n: usize) -> Result<Self, ResultBucketError> {
|
||||||
// Check heap size
|
// Check heap size
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
Err(BfsResolverError::BadBinHeapSize(n))
|
Err(ResultBucketError::BadBinHeapSize(n))
|
||||||
} else {
|
} else {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
n,
|
n,
|
||||||
@@ -182,6 +329,11 @@ impl ResultBucket {
|
|||||||
///
|
///
|
||||||
/// Returns `true` if the item was inserted, `false` otherwise.
|
/// Returns `true` if the item was inserted, `false` otherwise.
|
||||||
pub fn insert(&mut self, item: BfsItem, score: f64) -> bool {
|
pub fn insert(&mut self, item: BfsItem, score: f64) -> bool {
|
||||||
|
// YYC MARK:
|
||||||
|
// Because this struct stored `n` is must greater than zero,
|
||||||
|
// so after the first `if` branch, the length of this binary heap must be greater than zero.
|
||||||
|
// So there must be at least one item in binary heap.
|
||||||
|
// and we can safely use `expect()` to peek from binary heap.
|
||||||
let entry = ResultBucketItem::new(score, item, self.counter);
|
let entry = ResultBucketItem::new(score, item, self.counter);
|
||||||
if self.heap.len() < self.n {
|
if self.heap.len() < self.n {
|
||||||
self.heap.push(entry);
|
self.heap.push(entry);
|
||||||
@@ -205,129 +357,4 @@ impl ResultBucket {
|
|||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
/// A resolver that uses brute-force search to find the best matching circuits.
|
// endregion
|
||||||
pub struct BfsResolver {
|
|
||||||
/// The datasets for all device kinds.
|
|
||||||
datasets: SpecCatalog,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BfsResolver {
|
|
||||||
// YYC MARK:
|
|
||||||
// Some circuit are equivalent in topology.
|
|
||||||
// If we deduplicate these equaivalent circuit in building result,
|
|
||||||
// there are too complex works.
|
|
||||||
// So we should deduplicated these equivalent circuit at the beginning,
|
|
||||||
// i.e. when generating them.
|
|
||||||
// So following iterator structs are taking this job.
|
|
||||||
|
|
||||||
/// Iterate all possible circuits with one device without repeating equivalent topology.
|
|
||||||
pub fn iter_one_device_circuit(
|
|
||||||
dataset: &SpecGroup,
|
|
||||||
) -> impl Iterator<Item = Result<Circuit, CircuitError>> {
|
|
||||||
// Every single device is unique so we directly output them.
|
|
||||||
// This feature is insured by dataset itself.
|
|
||||||
dataset.specs().map(|v1| Circuit::from_one_device(v1))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterate all possible circuits with two devices without repeating equivalent topology.
|
|
||||||
pub fn iter_two_devices_circuit(
|
|
||||||
dataset: &SpecGroup,
|
|
||||||
) -> impl Iterator<Item = Result<Circuit, CircuitError>> {
|
|
||||||
// The two devices in this circuit is always swapable,
|
|
||||||
// so we iterate them without repeating.
|
|
||||||
itertools::iproduct!(
|
|
||||||
dataset.specs().array_combinations_with_replacement::<2>(),
|
|
||||||
JointKind::iter()
|
|
||||||
)
|
|
||||||
.map(|([v1, v2], j2)| Circuit::from_two_devices(v1, v2, j2))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterate all possible circuits with three devices without repeating equivalent topology.
|
|
||||||
pub fn iter_three_devices_circuit(
|
|
||||||
dataset: &SpecGroup,
|
|
||||||
) -> impl Iterator<Item = Result<Circuit, CircuitError>> {
|
|
||||||
// For generating three devices circuit,
|
|
||||||
// it should be consisted by 2 parts.
|
|
||||||
itertools::chain!(
|
|
||||||
// First, the whole circuit has only one joint type.
|
|
||||||
// In this case, 3 devices are swapable and we should iterate them without repeating
|
|
||||||
itertools::iproduct!(
|
|
||||||
dataset.specs().array_combinations_with_replacement::<3>(),
|
|
||||||
JointKind::iter()
|
|
||||||
)
|
|
||||||
.map(|([v1, v2, v3], j)| Circuit::from_three_devices(v1, v2, j, v3, j)),
|
|
||||||
// Second, if the joint type is different, then the first 2 devices are swapable.
|
|
||||||
// So we need iterate them without repeating.
|
|
||||||
itertools::iproduct!(
|
|
||||||
dataset.specs().array_combinations_with_replacement::<2>(),
|
|
||||||
dataset.specs(),
|
|
||||||
JointKind::iter()
|
|
||||||
)
|
|
||||||
.map(|([v1, v2], v3, j)| Circuit::from_three_devices(
|
|
||||||
v1,
|
|
||||||
v2,
|
|
||||||
j,
|
|
||||||
v3,
|
|
||||||
j.flip()
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BfsResolver {
|
|
||||||
/// Create a new BFS resolver with the given datasets.
|
|
||||||
pub fn new(datasets: SpecCatalog) -> Self {
|
|
||||||
Self { datasets }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pick_dataset(&self, device_kind: DeviceKind) -> &SpecGroup {
|
|
||||||
match device_kind {
|
|
||||||
DeviceKind::Resistor => self.datasets.resistor_specs(),
|
|
||||||
DeviceKind::Capacitor => self.datasets.capacitor_specs(),
|
|
||||||
DeviceKind::Inductor => self.datasets.inductor_specs(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bfs_iteration(
|
|
||||||
dataset: &SpecGroup,
|
|
||||||
ccalc: &CircuitCalculator,
|
|
||||||
) -> impl Iterator<Item = Result<BfsItem, BfsResolverError>> {
|
|
||||||
itertools::chain!(
|
|
||||||
BfsResolver::iter_one_device_circuit(&dataset),
|
|
||||||
BfsResolver::iter_two_devices_circuit(&dataset),
|
|
||||||
BfsResolver::iter_three_devices_circuit(&dataset)
|
|
||||||
)
|
|
||||||
.map(|circuit| -> Result<BfsItem, BfsResolverError> { BfsItem::new(circuit?, ccalc) })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn intern_resolve(&self, request: &Request) -> Result<Response, BfsResolverError> {
|
|
||||||
// Pick dataset from collection
|
|
||||||
let dataset = self.pick_dataset(request.get_device_kind());
|
|
||||||
|
|
||||||
// Iterate circuit item one by one
|
|
||||||
let mut bucket = ResultBucket::new(request.get_count_limit())?;
|
|
||||||
let ccalc = CircuitCalculator::new(request.get_device_kind(), request.get_target_value())?;
|
|
||||||
|
|
||||||
for item in BfsResolver::bfs_iteration(dataset, &ccalc) {
|
|
||||||
let item = item?;
|
|
||||||
// If circuit absolute difference is out of tolerance, skip it directly.
|
|
||||||
if item.unsigned_difference() <= request.get_tolerance() {
|
|
||||||
// Put it into bucket
|
|
||||||
let score = item.unsigned_difference();
|
|
||||||
bucket.insert(item, score);
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return result
|
|
||||||
let circuits = bucket.into_iter().map(|i| i.into_circuit());
|
|
||||||
Ok(Response::new(request, circuits)?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Resolver for BfsResolver {
|
|
||||||
fn resolve(&self, request: &Request) -> Result<Response, ResolverError> {
|
|
||||||
Ok(self.intern_resolve(request)?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ impl SpecGroup {
|
|||||||
Self::from_iterator([
|
Self::from_iterator([
|
||||||
"100", "220", "270", "390", "470", "680", "1k", "1.2k", "1.5k", "2.2k", "3.3k", "4.7k",
|
"100", "220", "270", "390", "470", "680", "1k", "1.2k", "1.5k", "2.2k", "3.3k", "4.7k",
|
||||||
"6.8k", "10k", "47k", "100k", "1M",
|
"6.8k", "10k", "47k", "100k", "1M",
|
||||||
]).expect("unexpected bad rated values set")
|
]).expect("unexpected bad rated values preset")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A commonly used set of capacitor rated values.
|
/// A commonly used set of capacitor rated values.
|
||||||
@@ -164,7 +164,7 @@ impl SpecGroup {
|
|||||||
Self::from_iterator([
|
Self::from_iterator([
|
||||||
"10p", "22p", "33p", "47p", "68p", "100p", "150p", "220p", "330p", "470p", "560p",
|
"10p", "22p", "33p", "47p", "68p", "100p", "150p", "220p", "330p", "470p", "560p",
|
||||||
"1u", "2.2u", "3.3u", "4.7u", "10u", "22u", "47u", "100u", "220u", "470u",
|
"1u", "2.2u", "3.3u", "4.7u", "10u", "22u", "47u", "100u", "220u", "470u",
|
||||||
]).expect("unexpected bad rated values set")
|
]).expect("unexpected bad rated values preset")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A commonly used set of inductor rated values.
|
/// A commonly used set of inductor rated values.
|
||||||
@@ -172,7 +172,7 @@ impl SpecGroup {
|
|||||||
Self::from_iterator([
|
Self::from_iterator([
|
||||||
"0.1u", "0.15u", "0.47u", "0.68u", "1u", "1.5u", "2.2u", "3.3u", "4.7u", "6.8u",
|
"0.1u", "0.15u", "0.47u", "0.68u", "1u", "1.5u", "2.2u", "3.3u", "4.7u", "6.8u",
|
||||||
"8.2u", "10u", "15u", "22u", "33u", "47u", "68u", "100u",
|
"8.2u", "10u", "15u", "22u", "33u", "47u", "68u", "100u",
|
||||||
]).expect("unexpected bad rated values set")
|
]).expect("unexpected bad rated values preset")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save(&self) -> impl Iterator<Item = &str> {
|
fn save(&self) -> impl Iterator<Item = &str> {
|
||||||
@@ -218,7 +218,7 @@ impl SpecGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over all numeric rated values in this group.
|
/// Iterate over all numeric rated values in this group.
|
||||||
pub fn specs(&self) -> impl Iterator<Item = f64> + Clone {
|
pub fn iter(&self) -> impl Iterator<Item = f64> + Clone {
|
||||||
self.specs.iter().map(|i| i.value)
|
self.specs.iter().map(|i| i.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
use lcrconn::spec;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_spec_preset() {
|
||||||
|
// All individual preset and catalog preset should nit panic
|
||||||
|
let specs = spec::SpecGroup::resistor_preset();
|
||||||
|
let specs = spec::SpecGroup::capacitor_preset();
|
||||||
|
let specs = spec::SpecGroup::inductor_preset();
|
||||||
|
|
||||||
|
let specs = spec::SpecCatalog::devices_preset();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user