use std::collections::HashSet;
use crate::{compact::CompactIntegerVector, point::Wire};
#[derive(Hash, PartialEq, Eq, Clone, Debug)]
pub struct CircuitLocation {
qudits: CompactIntegerVector,
dits: CompactIntegerVector,
}
impl CircuitLocation {
pub fn pure<T: AsRef<[usize]>>(location: T) -> CircuitLocation {
CircuitLocation::new(location, &[])
}
pub fn classical<T: AsRef<[usize]>>(location: T) -> CircuitLocation {
CircuitLocation::new(&[], location)
}
pub fn new<S: AsRef<[usize]>, T: AsRef<[usize]>>(qudits: S, dits: T) -> CircuitLocation {
let qudits = qudits.as_ref();
let dits = dits.as_ref();
if qudits.len() < 20 && dits.len() < 20 {
for i in 0..qudits.len() {
for j in (i + 1)..qudits.len() {
if qudits[i] == qudits[j] {
panic!("Duplicate indices in given circuit location.");
}
}
}
for i in 0..dits.len() {
for j in (i + 1)..dits.len() {
if dits[i] == dits[j] {
panic!("Duplicate indices in given circuit location.");
}
}
}
} else {
let mut uniq = HashSet::new();
if !qudits.iter().all(|x| uniq.insert(x)) {
panic!("Duplicate indices in given circuit location.");
}
uniq.clear();
if !dits.iter().all(|x| uniq.insert(x)) {
panic!("Duplicate indices in given circuit location.");
}
}
CircuitLocation {
qudits: CompactIntegerVector::from(qudits),
dits: CompactIntegerVector::from(dits),
}
}
pub fn qudits(&self) -> &CompactIntegerVector {
&self.qudits
}
pub fn dits(&self) -> &CompactIntegerVector {
&self.dits
}
pub fn union(&self, other: &CircuitLocation) -> CircuitLocation {
let mut union_qudits = self.qudits.clone();
for qudit_index in &other.qudits {
if !union_qudits.contains(qudit_index) {
union_qudits.push(qudit_index);
}
}
let mut union_dits = self.dits.clone();
for clbit_index in &other.dits {
if !union_dits.contains(clbit_index) {
union_dits.push(clbit_index);
}
}
CircuitLocation {
qudits: union_qudits,
dits: union_dits,
}
}
pub fn intersect(&self, other: &CircuitLocation) -> CircuitLocation {
let mut inter_qudits = CompactIntegerVector::new();
for qudit_index in &self.qudits {
if other.qudits.contains(qudit_index) {
inter_qudits.push(qudit_index);
}
}
let mut inter_dits = CompactIntegerVector::new();
for clbit_index in &self.dits {
if other.dits.contains(clbit_index) {
inter_dits.push(clbit_index);
}
}
CircuitLocation {
qudits: inter_qudits,
dits: inter_dits,
}
}
pub fn difference(&self, other: &CircuitLocation) -> CircuitLocation {
let mut diff_qudits = CompactIntegerVector::new();
for qudit_index in &self.qudits {
if !other.qudits.contains(qudit_index) {
diff_qudits.push(qudit_index);
}
}
let mut diff_dits = CompactIntegerVector::new();
for clbit_index in &self.dits {
if !other.dits.contains(clbit_index) {
diff_dits.push(clbit_index);
}
}
CircuitLocation {
qudits: diff_qudits,
dits: diff_dits,
}
}
pub fn get_qudit_pairs(&self) -> Vec<(usize, usize)> {
let num_pairs = self.qudits.len() * (self.qudits.len() - 1) / 2;
let mut to_return = Vec::with_capacity(num_pairs);
for qudit_index1 in &self.qudits {
for qudit_index2 in &self.qudits {
if qudit_index1 < qudit_index2 {
to_return.push((qudit_index1, qudit_index2));
}
}
}
to_return
}
pub fn to_sorted(&self) -> CircuitLocation {
let mut qudits_sorted = self.qudits.clone();
let mut dits_sorted = self.dits.clone();
qudits_sorted.sort();
dits_sorted.sort();
CircuitLocation {
qudits: qudits_sorted,
dits: dits_sorted,
}
}
pub fn is_sorted(&self) -> bool {
self.is_qudit_sorted() && self.is_dit_sorted()
}
pub fn is_qudit_sorted(&self) -> bool {
if self.qudits.len() < 2 {
return true;
}
(0..(self.qudits.len() - 1))
.all(|i| self.qudits.get(i).unwrap() < self.qudits.get(i + 1).unwrap())
}
pub fn is_dit_sorted(&self) -> bool {
if self.dits.len() < 2 {
return true;
}
(0..(self.dits.len() - 1))
.all(|i| self.dits.get(i).unwrap() < self.dits.get(i + 1).unwrap())
}
pub fn contains_qudit(&self, qudit_index: usize) -> bool {
self.qudits.contains(qudit_index)
}
pub fn contains_dit(&self, dit_index: usize) -> bool {
self.dits.contains(dit_index)
}
pub fn len(&self) -> usize {
self.get_num_qudits() + self.get_num_dits()
}
pub fn get_num_qudits(&self) -> usize {
self.qudits.len()
}
pub fn get_num_dits(&self) -> usize {
self.dits.len()
}
pub fn get_qudit_index(&self, index: usize) -> Option<usize> {
self.qudits.iter().position(|x| x == index)
}
pub fn get_dit_index(&self, index: usize) -> Option<usize> {
self.dits.iter().position(|x| x == index)
}
pub fn to_owned(&self) -> CircuitLocation {
let qudits = self.qudits.to_owned();
let dits = self.dits.to_owned();
CircuitLocation { qudits, dits }
}
pub fn wires(&self) -> Vec<Wire> {
self.qudits.iter().map(|q| Wire::quantum(q)).chain(self.dits.iter().map(|c| Wire::classical(c))).collect()
}
pub fn wires_iter(&self) -> impl Iterator<Item = Wire> + '_ {
self.qudits.iter().map(|q| Wire::quantum(q)).chain(self.dits.iter().map(|c| Wire::classical(c)))
}
}
pub trait ToLocation {
fn to_location(self) -> CircuitLocation;
}
impl<'a> ToLocation for &'a CircuitLocation {
fn to_location(self) -> CircuitLocation {
self.clone()
}
}
impl ToLocation for usize
{
fn to_location(self) -> CircuitLocation {
CircuitLocation::pure([self])
}
}
impl ToLocation for Vec<usize>
{
fn to_location(self) -> CircuitLocation {
CircuitLocation::pure(self)
}
}
impl<'a> ToLocation for &'a [usize]
{
fn to_location(self) -> CircuitLocation {
CircuitLocation::pure(self)
}
}
impl<const N: usize> ToLocation for [usize; N]
{
fn to_location(self) -> CircuitLocation {
CircuitLocation::pure(self)
}
}
impl<const N: usize> ToLocation for &[usize; N]
{
fn to_location(self) -> CircuitLocation {
CircuitLocation::pure(self)
}
}
impl ToLocation for (usize, usize)
{
fn to_location(self) -> CircuitLocation {
CircuitLocation::new([self.0], [self.1])
}
}
impl ToLocation for (Vec<usize>, Vec<usize>)
{
fn to_location(self) -> CircuitLocation {
CircuitLocation::new(self.0, self.1)
}
}
impl<'a, 'b> ToLocation for (&'a [usize], &'b [usize])
{
fn to_location(self) -> CircuitLocation {
CircuitLocation::new(self.0, self.1)
}
}
impl<const N: usize, const M: usize> ToLocation for ([usize; N], [usize; M])
{
fn to_location(self) -> CircuitLocation {
CircuitLocation::new(self.0, self.1)
}
}
impl<'a, 'b, const N: usize, const M: usize> ToLocation for (&'a [usize; N], &'b [usize; M])
{
fn to_location(self) -> CircuitLocation {
CircuitLocation::new(self.0, self.1)
}
}
impl<L: ToLocation> From<L> for CircuitLocation {
fn from(value: L) -> Self {
value.to_location()
}
}