use std::collections::HashMap;
use std::collections::hash_map::Entry;
use qudit_core::ParamIndices;
use rustc_hash::FxHashMap;
use serde::Deserialize;
use serde::Serialize;
use super::ArgumentList;
use super::NameOrParameter;
use super::Parameter;
use super::Value;
pub type ParameterId = usize;
pub type ParameterIndex = usize;
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct ParameterVector {
params: Vec<Parameter>,
named_param_ids: HashMap<String, ParameterId>,
id_to_index: FxHashMap<ParameterId, ParameterIndex>,
ref_counts: FxHashMap<ParameterId, usize>,
id_counter: ParameterId,
}
impl ParameterVector {
#[inline(always)]
pub fn len(&self) -> usize {
self.params.len()
}
pub fn num_unassigned(&self) -> usize {
self.params.iter().filter(|p| !p.is_assigned()).count()
}
#[inline(always)]
fn is_valid_id(&self, id: &ParameterId) -> bool {
self.id_to_index.contains_key(id)
}
#[inline(always)]
fn increment(&mut self, param_id: ParameterId) {
self.ref_counts
.entry(param_id)
.and_modify(|count| *count += 1)
.or_insert(1);
}
#[inline(always)]
pub(crate) fn decrement(&mut self, param_id: ParameterId) {
if let Entry::Occupied(mut entry) = self.ref_counts.entry(param_id) {
let count = entry.get_mut();
*count -= 1;
if *count == 0 {
entry.remove();
}
}
}
#[inline(always)]
fn push(&mut self, param: NameOrParameter) -> ParameterId {
let id = {
let new_id = self.id_counter;
self.id_counter += 1;
self.id_to_index.insert(new_id, self.params.len());
self.increment(new_id);
new_id
};
match param {
NameOrParameter::Name(name) => {
self.named_param_ids.insert(name.clone(), id);
self.params.push(Parameter::Unassigned);
}
NameOrParameter::Parameter(param) => {
self.params.push(param);
}
}
id
}
pub fn parse(&mut self, args: &ArgumentList) -> ParamIndices {
if args.is_empty() {
return ParamIndices::empty();
}
let parameter_vector = args.parameters();
let mut param_indices = vec![];
let mut joint = true;
for param in parameter_vector.into_iter() {
if let NameOrParameter::Name(name) = ¶m {
if name.contains("param_") {
if let Some(s) = name.strip_prefix("param_") {
if let Ok(id) = s.parse::<ParameterId>() {
if self.is_valid_id(&id) {
param_indices.push(id);
self.increment(id);
joint = false;
continue;
}
}
}
panic!("Cannot provide a parameter name containing 'param_'");
}
if let Some(id) = self.named_param_ids.get(name) {
param_indices.push(*id);
self.increment(*id);
joint = false;
continue;
}
}
param_indices.push(self.push(param));
}
match joint {
true => ParamIndices::Joint(param_indices[0], param_indices.len()),
false => ParamIndices::Disjoint(param_indices),
}
}
pub fn convert_ids_to_indices(&self, param_ids: ParamIndices) -> ParamIndices {
if param_ids.is_empty() {
return ParamIndices::empty();
}
let indices: Vec<_> = param_ids
.iter()
.map(|id| {
*self
.id_to_index
.get(&id)
.expect("Cannot find parameter id.")
})
.collect();
if indices.len() == 1 {
return ParamIndices::Joint(indices[0], 1);
}
let consecutive = indices.windows(2).all(|w| w[1] == w[0] + 1);
if consecutive {
ParamIndices::Joint(indices[0], indices.len())
} else {
ParamIndices::Disjoint(indices)
}
}
pub fn const_map<R: qudit_core::RealScalar>(&self) -> FxHashMap<usize, R> {
let mut const_map = FxHashMap::default();
for (i, param) in self.params.iter().enumerate() {
if param.is_assigned() {
const_map.insert(i, param.extract_float().expect("Unexpected non-constant."));
}
}
const_map
}
pub fn assign_by_id(&mut self, param_id: &ParameterId, value: impl Into<Value>) {
if let Some(idx) = self.id_to_index.get(param_id) {
self.params[*idx] = value.into().into();
}
}
pub fn assign_by_name(&mut self, name: impl AsRef<str>, value: impl Into<Value>) {
if let Some(&id) = self.named_param_ids.get(name.as_ref()) {
self.assign_by_id(&id, value);
}
}
pub fn assign_all(&mut self, values: Vec<impl Into<Value>>) {
for (param, value) in self
.params
.iter_mut()
.filter(|x| !x.is_assigned())
.zip(values)
{
*param = value.into().into()
}
}
}
impl std::ops::Deref for ParameterVector {
type Target = [Parameter];
fn deref(&self) -> &Self::Target {
&self.params
}
}