use std::cell::RefCell;
use std::thread::LocalKey;
use std::sync::Mutex;
use crate::AD;
use crate::ADfn;
#[cfg(doc)]
use crate::doc_generic_v;
pub type IndexT = u32;
pub struct Tape<V> {
pub recording : bool,
pub tape_id : usize,
pub n_domain : usize,
pub n_var : usize,
pub id_all : Vec<u8>,
pub op2arg : Vec<IndexT>,
pub arg_all : Vec<IndexT>,
pub con_all : Vec<V>,
pub flag_all : Vec<bool>,
}
impl<V> Tape<V> {
pub fn new() -> Self {
Self {
recording : false,
tape_id : 0,
n_domain : 0,
n_var : 0,
id_all : Vec::new() ,
op2arg : Vec::new() ,
arg_all : Vec::new() ,
con_all : Vec::new() ,
flag_all : Vec::new() ,
}
}
}
pub(crate) static NEXT_TAPE_ID : Mutex<usize> = Mutex::new(1);
pub (crate) mod sealed {
use super::Tape;
use std::cell::RefCell;
use std::thread::LocalKey;
pub trait ThisThreadTape
where
Self : Sized + 'static ,
{
fn get() -> &'static LocalKey< RefCell< Tape<Self> > >;
}
}
macro_rules! impl_this_thread_tape{ ($V:ty) => {
#[doc = concat!(
"This threads tape for recording ",
"`AD<" , stringify!($V), ">` operations"
) ]
impl crate::tape::sealed::ThisThreadTape for $V {
fn get() -> &'static LocalKey<
RefCell< crate::tape::Tape<$V> >
> {
thread_local! {
pub(crate) static THIS_THREAD_TAPE : RefCell<
crate::tape::Tape<$V>
> = RefCell::new( crate::tape::Tape::new() );
}
&THIS_THREAD_TAPE
}
}
} }
pub(crate) use impl_this_thread_tape;
pub fn start_recording<V>(domain : Vec<V> ) -> Vec< AD<V> >
where
V : Clone + Sized + 'static + sealed::ThisThreadTape ,
{
let tape_id : usize;
{ let mut next_tape_id = NEXT_TAPE_ID.lock().unwrap();
tape_id = *next_tape_id;
*next_tape_id += 1;
}
let local_key : &LocalKey< RefCell< Tape<V> > > =
sealed::ThisThreadTape::get();
local_key.with_borrow_mut( |tape| {
assert_ne!( tape_id, 0);
assert!( ! tape.recording ,
"start_recording: This thread's tape is already recording"
);
assert_eq!( tape.id_all.len(), 0 );
assert_eq!( tape.op2arg.len(), 0 );
assert_eq!( tape.arg_all.len(), 0 );
assert_eq!( tape.con_all.len(), 0 );
assert_eq!( tape.flag_all.len(), 0 );
tape.tape_id = tape_id;
tape.recording = true;
tape.n_domain = domain.len();
tape.n_var = domain.len();
} );
let adomain = domain.into_iter().enumerate().map(
| (index, value) | AD::new(tape_id, index, value)
).collect();
adomain
}
pub fn stop_recording<V>( arange : Vec< AD<V> > ) -> ADfn<V>
where
IndexT : TryFrom<usize> ,
V : Clone + Sized + 'static + sealed::ThisThreadTape ,
{
let mut ad_fn : ADfn<V> = ADfn::new();
let local_key : &LocalKey< RefCell< Tape<V> > > =
sealed::ThisThreadTape::get();
let tape_id : usize = local_key.with_borrow_mut( |tape| {
assert!( tape.recording ,
"stop_recording: This thread's tape is not recording"
);
tape.recording = false;
match IndexT::try_from( tape.arg_all.len() ) {
Err(_) => panic!( "tape.arg_all.len() > IndexT::MAX" ),
Ok(_) => (),
}
match IndexT::try_from( tape.tape_id ) {
Err(_) => panic!( "tape.tape_id > IndexT::MAX" ),
Ok(_) => (),
}
let con_all_len = tape.con_all.len() + arange.len();
match IndexT::try_from( con_all_len ) {
Err(_) => panic!(
"tape.con_all.len() + arange.len() > IndexT::MAX"
),
Ok(_) => (),
}
assert_eq!( tape.op2arg.len() , tape.id_all.len() );
assert_eq!( tape.n_var , tape.n_domain + tape.id_all.len() );
tape.op2arg.push( tape.arg_all.len() as IndexT );
std::mem::swap( &mut ad_fn.n_domain, &mut tape.n_domain );
std::mem::swap( &mut ad_fn.n_var, &mut tape.n_var );
std::mem::swap( &mut ad_fn.id_all, &mut tape.id_all );
std::mem::swap( &mut ad_fn.op2arg, &mut tape.op2arg );
std::mem::swap( &mut ad_fn.arg_all, &mut tape.arg_all );
std::mem::swap( &mut ad_fn.con_all, &mut tape.con_all );
std::mem::swap( &mut ad_fn.flag_all, &mut tape.flag_all );
tape.tape_id
} );
for i in 0 .. arange.len() {
if arange[i].tape_id == tape_id {
ad_fn.range_is_var.push( true );
ad_fn.range2tape_index.push( arange[i].var_index as IndexT );
} else {
ad_fn.range_is_var.push( false );
ad_fn.range2tape_index.push( ad_fn.con_all.len() as IndexT );
ad_fn.con_all.push( arange[i].value.clone() );
}
}
ad_fn
}