1use demes_forward::demes;
2use libc::c_char;
3use std::ffi::CStr;
4use std::ffi::CString;
5use std::io::Read;
6
7pub struct OpaqueForwardGraph {
24 graph: Option<demes_forward::ForwardGraph>,
25 error: Option<CString>,
26 current_time: Option<f64>,
27 deny_send_sync: std::marker::PhantomData<*const ()>,
32}
33
34#[repr(i32)]
35enum ErrorCode {
36 GraphIsNull = -2,
38}
39
40impl OpaqueForwardGraph {
41 fn update(&mut self, graph: Option<demes_forward::ForwardGraph>, error: Option<String>) {
42 self.graph = graph;
43 self.update_error(error);
44 }
45
46 fn update_error(&mut self, error: Option<String>) {
47 self.error = error.map(|e| {
48 CString::new(
49 e.chars()
50 .filter(|c| c.is_ascii() && c != &'"')
51 .collect::<String>(),
52 )
53 .unwrap()
54 });
55 }
56}
57
58#[no_mangle]
69pub extern "C" fn forward_graph_allocate() -> *mut OpaqueForwardGraph {
70 Box::into_raw(Box::new(OpaqueForwardGraph {
71 graph: None,
72 error: None,
73 current_time: None,
74 deny_send_sync: std::marker::PhantomData,
75 }))
76}
77
78unsafe fn yaml_to_owned(yaml: *const c_char) -> Option<String> {
79 if yaml.is_null() {
80 return None;
81 }
82 let yaml = CStr::from_ptr(yaml);
83 match yaml.to_owned().to_str() {
84 Ok(s) => Some(s.to_owned()),
85 Err(_) => None,
86 }
87}
88
89unsafe fn process_input_yaml(
90 yaml: *const c_char,
91 graph: *mut OpaqueForwardGraph,
92) -> (i32, Option<demes::Graph>) {
93 if graph.is_null() {
94 return (ErrorCode::GraphIsNull as i32, None);
95 }
96 let yaml = match yaml_to_owned(yaml) {
97 Some(s) => s,
98 None => {
99 (*graph).update(None, Some("could not convert c_char to String".to_string()));
100 return (-1, None);
101 }
102 };
103 match demes::loads(&yaml) {
104 Ok(graph) => (0, Some(graph)),
105 Err(e) => {
106 (*graph).update(None, Some(format!("{e}")));
107 (-1, None)
108 }
109 }
110}
111
112#[no_mangle]
119pub unsafe extern "C" fn forward_graph_initialize_from_yaml(
120 yaml: *const c_char,
121 burnin: f64,
122 graph: *mut OpaqueForwardGraph,
123) -> i32 {
124 let dg = match process_input_yaml(yaml, graph) {
125 (x, Some(g)) => {
126 assert_eq!(x, 0);
127 g
128 }
129 (y, None) => {
130 assert!(y < 0);
131 return -1;
132 }
133 };
134
135 match demes_forward::ForwardGraph::new_discrete_time(dg, burnin) {
136 Ok(fgraph) => (*graph).update(Some(fgraph), None),
137 Err(e) => (*graph).update(None, Some(format!("{e}"))),
138 };
139 0
140}
141
142#[no_mangle]
153pub unsafe extern "C" fn forward_graph_initialize_from_yaml_round_epoch_sizes(
154 yaml: *const c_char,
155 burnin: f64,
156 graph: *mut OpaqueForwardGraph,
157) -> i32 {
158 let dg = match process_input_yaml(yaml, graph) {
159 (x, Some(g)) => {
160 assert_eq!(x, 0);
161 g
162 }
163 (y, None) => {
164 assert!(y < 0);
165 return -1;
166 }
167 };
168 let dg = match dg.into_integer_start_end_sizes() {
169 Ok(graph) => graph,
170 Err(e) => {
171 (*graph).update(None, Some(format!("{e}")));
172 return -1;
173 }
174 };
175 match demes_forward::ForwardGraph::new_discrete_time(dg, burnin) {
176 Ok(fgraph) => (*graph).update(Some(fgraph), None),
177 Err(e) => (*graph).update(None, Some(format!("{e}"))),
178 };
179 0
180}
181
182#[no_mangle]
187pub unsafe extern "C" fn forward_graph_initialize_from_yaml_file(
188 file_name: *const c_char,
189 burnin: f64,
190 graph: *mut OpaqueForwardGraph,
191) -> i32 {
192 if graph.is_null() {
193 return ErrorCode::GraphIsNull as i32;
194 }
195 let filename_cstr = CStr::from_ptr(file_name);
196 let filename = match filename_cstr.to_str() {
197 Ok(string) => string,
198 Err(e) => {
199 (*graph).update(None, Some(format!("{e}")));
200 return -1;
201 }
202 };
203 match std::fs::File::open(filename) {
204 Ok(mut file) => {
205 let mut buf = String::default();
206 match file.read_to_string(&mut buf) {
207 Ok(_) => {
208 let cstring = CString::new(buf).unwrap();
209 let ptr = cstring.as_ptr();
210 forward_graph_initialize_from_yaml(ptr, burnin, graph)
211 }
212 Err(e) => {
213 (*graph).update(None, Some(format!("{e}")));
214 -1
215 }
216 }
217 }
218 Err(e) => {
219 (*graph).update(None, Some(format!("{e}")));
220 -1
221 }
222 }
223}
224
225#[no_mangle]
229pub unsafe extern "C" fn forward_graph_is_error_state(graph: *const OpaqueForwardGraph) -> bool {
230 (*graph).error.is_some()
231}
232
233#[no_mangle]
237pub unsafe extern "C" fn forward_graph_deallocate(graph: *mut OpaqueForwardGraph) {
238 let _ = Box::from_raw(graph);
239}
240
241#[no_mangle]
245pub unsafe extern "C" fn forward_graph_get_error_message(
246 graph: *const OpaqueForwardGraph,
247 status: *mut i32,
248) -> *const c_char {
249 *status = 0;
250 if !graph.is_null() {
251 match &(*graph).error {
252 Some(message) => message.as_ptr(),
253 None => std::ptr::null(),
254 }
255 } else {
256 *status = ErrorCode::GraphIsNull as i32;
257 std::ptr::null()
258 }
259}
260
261#[no_mangle]
269pub unsafe extern "C" fn forward_graph_selfing_rates(
270 graph: *const OpaqueForwardGraph,
271 status: *mut i32,
272) -> *const f64 {
273 *status = 0;
274 if !graph.is_null() {
275 match &(*graph).graph {
276 Some(graph) => match graph.selfing_rates() {
277 Some(slice) => slice.as_ptr() as *const f64,
278 None => std::ptr::null(),
279 },
280 None => {
281 *status = -1;
282 std::ptr::null()
283 }
284 }
285 } else {
286 *status = ErrorCode::GraphIsNull as i32;
287 std::ptr::null()
288 }
289}
290
291#[no_mangle]
299pub unsafe extern "C" fn forward_graph_cloning_rates(
300 graph: *const OpaqueForwardGraph,
301 status: *mut i32,
302) -> *const f64 {
303 *status = 0;
304 if !graph.is_null() {
305 match &(*graph).graph {
306 Some(graph) => match graph.cloning_rates() {
307 Some(slice) => slice.as_ptr() as *const f64,
308 None => std::ptr::null(),
309 },
310 None => {
311 *status = -1;
312 std::ptr::null()
313 }
314 }
315 } else {
316 *status = ErrorCode::GraphIsNull as i32;
317 std::ptr::null()
318 }
319}
320
321#[no_mangle]
329pub unsafe extern "C" fn forward_graph_parental_deme_sizes(
330 graph: *const OpaqueForwardGraph,
331 status: *mut i32,
332) -> *const f64 {
333 *status = 0;
334 if !graph.is_null() {
335 match &(*graph).graph {
336 Some(graph) => match graph.parental_deme_sizes() {
337 Some(slice) => slice.as_ptr() as *const f64,
338 None => std::ptr::null(),
339 },
340 None => {
341 *status = -1;
342 std::ptr::null()
343 }
344 }
345 } else {
346 *status = ErrorCode::GraphIsNull as i32;
347 std::ptr::null()
348 }
349}
350
351#[no_mangle]
359pub unsafe extern "C" fn forward_graph_offspring_deme_sizes(
360 graph: *const OpaqueForwardGraph,
361 status: *mut i32,
362) -> *const f64 {
363 *status = 0;
364 if !graph.is_null() {
365 match &(*graph).graph {
366 Some(graph) => match graph.offspring_deme_sizes() {
367 Some(slice) => slice.as_ptr() as *const f64,
368 None => std::ptr::null(),
369 },
370 None => {
371 *status = -1;
372 std::ptr::null()
373 }
374 }
375 } else {
376 *status = ErrorCode::GraphIsNull as i32;
377 std::ptr::null()
378 }
379}
380
381#[no_mangle]
387pub unsafe extern "C" fn forward_graph_any_extant_offspring_demes(
388 graph: *const OpaqueForwardGraph,
389 status: *mut i32,
390) -> bool {
391 *status = 0;
392 if !graph.is_null() {
393 match &(*graph).graph {
394 Some(graph) => graph.any_extant_offspring_demes(),
395 None => {
396 *status = -1;
397 false
398 }
399 }
400 } else {
401 *status = ErrorCode::GraphIsNull as i32;
402 false
403 }
404}
405
406#[no_mangle]
412pub unsafe extern "C" fn forward_graph_any_extant_parent_demes(
413 graph: *const OpaqueForwardGraph,
414 status: *mut i32,
415) -> bool {
416 *status = 0;
417 if !graph.is_null() {
418 match &(*graph).graph {
419 Some(graph) => graph.any_extant_parental_demes(),
420 None => {
421 *status = -1;
422 false
423 }
424 }
425 } else {
426 *status = ErrorCode::GraphIsNull as i32;
427 false
428 }
429}
430
431#[no_mangle]
442pub unsafe extern "C" fn forward_graph_number_of_demes(graph: *const OpaqueForwardGraph) -> isize {
443 match &(*graph).graph {
444 Some(graph) => graph.num_demes_in_model() as isize,
445 None => -1,
446 }
447}
448
449#[no_mangle]
455pub unsafe extern "C" fn forward_graph_update_state(
456 time: f64,
457 graph: *mut OpaqueForwardGraph,
458) -> i32 {
459 if !graph.is_null() {
460 match &mut (*graph).graph {
461 Some(fgraph) => match fgraph.update_state(time) {
462 Ok(()) => 0,
463 Err(e) => {
464 (*graph).update(None, Some(format!("{e}")));
465 -1
466 }
467 },
468 None => -1,
469 }
470 } else {
471 ErrorCode::GraphIsNull as i32
472 }
473}
474
475#[no_mangle]
481pub unsafe extern "C" fn forward_graph_initialize_time_iteration(
482 graph: *mut OpaqueForwardGraph,
483) -> i32 {
484 if !graph.is_null() {
485 match &mut (*graph).graph {
486 Some(fgraph) => {
487 match fgraph.last_time_updated() {
488 Some(value) => {
489 (*graph).current_time = Some(value.value() - 1.0);
490 }
491 None => {
492 (*graph).current_time = Some(-1.0);
493 }
494 }
495 0
496 }
497 None => -1,
498 }
499 } else {
500 ErrorCode::GraphIsNull as i32
501 }
502}
503
504#[no_mangle]
515pub unsafe extern "C" fn forward_graph_iterate_time(
516 graph: *mut OpaqueForwardGraph,
517 status: *mut i32,
518) -> *const f64 {
519 if graph.is_null() {
520 *status = ErrorCode::GraphIsNull as i32;
521 return std::ptr::null();
522 }
523 *status = 0;
524 if (*graph).current_time.is_none() {
525 *status = -1;
526 (*graph).update_error(Some(
527 "forward_graph_initialize_time_iteration has not been called".to_string(),
528 ));
529 return std::ptr::null();
530 }
531 let tref: &mut f64 = (*graph).current_time.as_mut().unwrap();
532 match &mut (*graph).graph {
533 Some(fgraph) => {
534 if *tref < fgraph.end_time().value() - 1.0 {
535 *tref += 1.0;
536 &*tref
537 } else {
538 (*graph).current_time = None;
539 std::ptr::null()
540 }
541 }
542 None => {
543 *status = -1;
544 std::ptr::null()
545 }
546 }
547}
548
549#[no_mangle]
554pub unsafe extern "C" fn forward_graph_ancestry_proportions(
555 offspring_deme: usize,
556 status: *mut i32,
557 graph: *mut OpaqueForwardGraph,
558) -> *const f64 {
559 if graph.is_null() {
560 *status = ErrorCode::GraphIsNull as i32;
561 return std::ptr::null();
562 }
563 *status = 0;
564 if (*graph).error.is_some() {
565 *status = -1;
566 return std::ptr::null();
567 }
568 match &(*graph).graph {
569 Some(fgraph) => {
570 if offspring_deme >= fgraph.num_demes_in_model() {
571 *status = -1;
572 (*graph).update_error(Some(format!(
573 "offspring deme index {} out of range",
574 offspring_deme
575 )));
576 std::ptr::null()
577 } else {
578 match fgraph.ancestry_proportions(offspring_deme) {
579 Some(proportions) => proportions.as_ptr(),
580 None => std::ptr::null(),
581 }
582 }
583 }
584 None => {
585 *status = -1;
586 std::ptr::null()
587 }
588 }
589}
590
591#[no_mangle]
603pub unsafe extern "C" fn forward_graph_model_end_time(
604 status: *mut i32,
605 graph: *const OpaqueForwardGraph,
606) -> f64 {
607 *status = 0;
608 if (*graph).error.is_some() || (*graph).graph.is_none() {
609 *status = -1;
610 f64::NAN
611 } else {
612 match &(*graph).graph {
613 Some(fgraph) => fgraph.end_time().value(),
614 None => {
615 *status = -1;
616 f64::NAN
617 }
618 }
619 }
620}
621
622#[no_mangle]
644pub unsafe extern "C" fn forward_graph_get_demes_graph(
645 graph: *const OpaqueForwardGraph,
646 status: *mut i32,
647) -> *const c_char {
648 match &(*graph).graph {
649 None => std::ptr::null(),
650 Some(g) => {
651 let demes_graph = match g.demes_graph().as_string() {
652 Ok(g) => g,
653 Err(_) => {
654 *status = -1;
655 return std::ptr::null();
656 }
657 };
658 let c_str = match CString::new(demes_graph) {
659 Ok(c) => c,
660 Err(_) => {
661 *status = -1;
662 return std::ptr::null();
663 }
664 };
665 *status = 0;
666 libc::strdup(c_str.as_ptr()) as *const c_char
667 }
668 }
669}
670
671#[cfg(test)]
672mod tests {
673 use super::*;
674 use std::{ffi::CString, io::Write};
675
676 struct GraphHolder {
677 graph: *mut OpaqueForwardGraph,
678 }
679
680 impl GraphHolder {
681 fn new() -> Self {
682 Self {
683 graph: forward_graph_allocate(),
684 }
685 }
686
687 fn as_mut_ptr(&mut self) -> *mut OpaqueForwardGraph {
688 self.graph
689 }
690
691 fn as_ptr(&mut self) -> *const OpaqueForwardGraph {
692 self.graph
693 }
694
695 fn init_with_yaml(&mut self, burnin: f64, yaml: &str) -> i32 {
696 let yaml_cstr = CString::new(yaml).unwrap();
697 let yaml_c_char: *const c_char = yaml_cstr.as_ptr() as *const c_char;
698 unsafe { forward_graph_initialize_from_yaml(yaml_c_char, burnin, self.as_mut_ptr()) }
699 }
700
701 fn init_with_yaml_round_epoch_sizes(&mut self, burnin: f64, yaml: &str) -> i32 {
702 let yaml_cstr = CString::new(yaml).unwrap();
703 let yaml_c_char: *const c_char = yaml_cstr.as_ptr() as *const c_char;
704 unsafe {
705 forward_graph_initialize_from_yaml_round_epoch_sizes(
706 yaml_c_char,
707 burnin,
708 self.as_mut_ptr(),
709 )
710 }
711 }
712 }
713
714 impl Drop for GraphHolder {
715 fn drop(&mut self) {
716 unsafe { forward_graph_deallocate(self.as_mut_ptr()) };
717 }
718 }
719
720 #[test]
721 fn test_invalid_graph() {
722 let yaml = "
723time_units: generations
724demes:
725 - name: A
726 start_time: 55
727 epochs:
728 - start_size: 100
729 end_time: 50
730 - start_size: 200
731";
732 let mut graph = GraphHolder::new();
733 graph.init_with_yaml(100.0, yaml);
734 assert!(unsafe { forward_graph_is_error_state(graph.as_ptr()) });
735 let mut status = -1;
736 let pstatus: *mut i32 = &mut status;
737 let message = unsafe { forward_graph_get_error_message(graph.as_ptr(), pstatus) };
738 assert_eq!(status, 0);
739 assert!(!message.is_null());
740 let rust_message = unsafe { CStr::from_ptr(message) };
741 let rust_message: &str = rust_message.to_str().unwrap();
742 assert_eq!(
743 rust_message,
744 "deme A has finite start time but no ancestors"
745 );
746 }
747
748 #[test]
749 fn test_empty_graph() {
750 let yaml = "";
751 let mut graph = GraphHolder::new();
752 graph.init_with_yaml(100.0, yaml);
753 assert!(unsafe { forward_graph_is_error_state(graph.as_ptr()) });
754 }
755
756 #[test]
757 fn test_null_graph() {
758 let yaml: *const c_char = std::ptr::null();
759 let graph = forward_graph_allocate();
760 unsafe { forward_graph_initialize_from_yaml(yaml, 100.0, graph) };
761 assert!(unsafe { forward_graph_is_error_state(graph) });
762 unsafe { forward_graph_deallocate(graph) };
763 }
764
765 #[test]
766 fn number_of_demes_in_model() {
767 {
768 let yaml = "
769time_units: generations
770demes:
771 - name: A
772 epochs:
773 - start_size: 100
774 end_time: 50
775 - start_size: 200
776";
777 {
778 let mut graph = GraphHolder::new();
779 graph.init_with_yaml(100.0, yaml);
780 let num_demes = unsafe { forward_graph_number_of_demes(graph.as_ptr()) };
781 assert_eq!(num_demes, 1);
782 }
783
784 {
786 let graph = forward_graph_allocate();
787 let cstr = CString::new(yaml).unwrap();
788 unsafe { forward_graph_initialize_from_yaml(cstr.as_ptr(), 100., graph) };
789 let num_demes = unsafe { forward_graph_number_of_demes(graph) };
790 assert_eq!(num_demes, 1);
791 unsafe { forward_graph_deallocate(graph) };
792 }
793 }
794 }
795
796 #[test]
797 fn iterate_simple_model() {
798 let yaml = "
799time_units: generations
800demes:
801 - name: A
802 epochs:
803 - start_size: 100
804 end_time: 50
805 - start_size: 200
806";
807 let mut graph = GraphHolder::new();
808 graph.init_with_yaml(100.0, yaml);
809 let mut status = -1;
810 assert!(unsafe { forward_graph_selfing_rates(graph.as_ptr(), &mut status) }.is_null());
811 assert_eq!(status, 0);
812 status = -1;
813 assert!(unsafe { forward_graph_cloning_rates(graph.as_ptr(), &mut status) }.is_null());
814 assert_eq!(status, 0);
815 status = -1;
816 assert!(
817 unsafe { forward_graph_parental_deme_sizes(graph.as_ptr(), &mut status) }.is_null(),
818 );
819 assert_eq!(status, 0);
820 status = -1;
821 assert!(
822 unsafe { forward_graph_offspring_deme_sizes(graph.as_ptr(), &mut status) }.is_null(),
823 );
824 assert_eq!(status, 0);
825 status = -1;
826 assert!(!unsafe { forward_graph_any_extant_offspring_demes(graph.as_ptr(), &mut status) });
827 assert_eq!(status, 0);
828 status = -1;
829 assert!(!unsafe { forward_graph_any_extant_parent_demes(graph.as_ptr(), &mut status) });
830 assert_eq!(status, 0);
831
832 {
833 assert_eq!(
834 unsafe { forward_graph_initialize_time_iteration(graph.as_mut_ptr()) },
835 0,
836 );
837 let mut ngens = -1_i32;
838 let mut ptime: *const f64;
839 let mut ancestry_proportions: *const f64;
840 let mut times = vec![];
841 let mut sizes = vec![100.0; 100];
842 sizes.append(&mut vec![200.0; 50]);
843
844 let mut status = -1;
845 let pstatus: *mut i32 = &mut status;
846 ptime = unsafe { forward_graph_iterate_time(graph.as_mut_ptr(), pstatus) };
847 assert_eq!(
848 unsafe { forward_graph_model_end_time(pstatus, graph.as_ptr()) },
849 151.0
850 );
851 assert_eq!(status, 0);
852 while !ptime.is_null() {
853 assert_eq!(status, 0);
854 ngens += 1;
855 unsafe { times.push(*ptime) };
856 assert_eq!(
857 unsafe { forward_graph_update_state(*ptime, graph.as_mut_ptr()) },
858 0,
859 );
860 let mut status = -1;
861 if unsafe { forward_graph_any_extant_offspring_demes(graph.as_ptr(), &mut status) }
862 {
863 assert_eq!(status, 0);
864 let offspring_deme_sizes =
865 unsafe { forward_graph_offspring_deme_sizes(graph.as_ptr(), &mut status) };
866 assert_eq!(status, 0);
867 assert!(!offspring_deme_sizes.is_null());
868 ancestry_proportions = unsafe {
869 forward_graph_ancestry_proportions(0, &mut status, graph.as_mut_ptr())
870 };
871 assert_eq!(status, 0);
872 let ancestry_proportions =
873 unsafe { std::slice::from_raw_parts(ancestry_proportions, 1) };
874 assert!((ancestry_proportions[0] - 1.0) <= 1e-9);
875 let deme_sizes = unsafe { std::slice::from_raw_parts(offspring_deme_sizes, 1) };
876 assert_eq!(deme_sizes[0], sizes[ngens as usize]);
877 } else {
878 status = -1;
879 let offspring_deme_sizes =
880 unsafe { forward_graph_offspring_deme_sizes(graph.as_ptr(), &mut status) };
881 assert_eq!(status, 0);
882 assert!(offspring_deme_sizes.is_null());
883 }
884 ptime = unsafe { forward_graph_iterate_time(graph.as_mut_ptr(), &mut status) };
885 }
886 assert!(ptime.is_null());
887 assert_eq!(times.first().unwrap(), &0.0);
888 assert_eq!(times.last().unwrap(), &150.0);
889 assert_eq!(ngens, 150);
890 }
891
892 {
894 assert_eq!(
895 unsafe { forward_graph_update_state(50.0, graph.as_mut_ptr()) },
896 0,
897 );
898 assert_eq!(
899 unsafe { forward_graph_initialize_time_iteration(graph.as_mut_ptr()) },
900 0,
901 );
902 let mut ngens = -1_i32;
903 let mut ptime: *const f64;
904 let mut times = vec![];
905 let mut sizes = vec![100.0; 50];
906 sizes.append(&mut vec![200.0; 50]);
907
908 let mut status = -1;
909 ptime = unsafe { forward_graph_iterate_time(graph.as_mut_ptr(), &mut status) };
910 while !ptime.is_null() {
911 assert_eq!(status, 0);
912 ngens += 1;
913 unsafe { times.push(*ptime) };
914 assert_eq!(
915 unsafe { forward_graph_update_state(*ptime, graph.as_mut_ptr()) },
916 0,
917 );
918 let mut status = -1;
919 if unsafe { forward_graph_any_extant_offspring_demes(graph.as_ptr(), &mut status) }
920 {
921 assert_eq!(status, 0);
922 let offspring_deme_sizes =
923 unsafe { forward_graph_offspring_deme_sizes(graph.as_ptr(), &mut status) };
924 assert_eq!(status, 0);
925 assert!(!offspring_deme_sizes.is_null());
926 let deme_sizes = unsafe { std::slice::from_raw_parts(offspring_deme_sizes, 1) };
927 assert_eq!(deme_sizes[0], sizes[ngens as usize]);
928 } else {
929 status = -1;
930 let offspring_deme_sizes =
931 unsafe { forward_graph_offspring_deme_sizes(graph.as_ptr(), &mut status) };
932 assert_eq!(status, 0);
933 assert!(offspring_deme_sizes.is_null());
934 }
935 ptime = unsafe { forward_graph_iterate_time(graph.as_mut_ptr(), &mut status) };
936 }
937 assert!(ptime.is_null());
938 assert_eq!(times.first().unwrap(), &50.0);
939 assert_eq!(times.last().unwrap(), &150.0);
940 assert_eq!(ngens, 100);
941 }
942 }
943
944 #[test]
945 fn test_from_yaml_file() {
946 let yaml = "
947time_units: generations
948demes:
949 - name: A
950 epochs:
951 - start_size: 100
952 end_time: 50
953 - start_size: 200
954";
955 {
956 let mut file = std::fs::File::create("simple_model.yaml").unwrap();
957 file.write_all(yaml.as_bytes()).unwrap();
958 }
959
960 let mut graph = GraphHolder::new();
961
962 let filename = "simple_model.yaml";
963 let filename_cstring = CString::new(filename).unwrap();
964 let filename: *const c_char = filename_cstring.as_ptr() as *const c_char;
965 assert_eq!(
966 unsafe { forward_graph_initialize_from_yaml_file(filename, 100.0, graph.as_mut_ptr()) },
967 0
968 );
969 let mut status = -1;
970 let pstatus: *mut i32 = &mut status;
971
972 assert_eq!(
973 unsafe { forward_graph_model_end_time(pstatus, graph.as_mut_ptr()) },
974 151.0
975 );
976
977 std::fs::remove_file("simple_model.yaml").unwrap();
978 }
979
980 #[test]
981 fn test_recover_demes_graph() {
982 let yaml = "
983time_units: generations
984demes:
985 - name: A
986 epochs:
987 - start_size: 100
988 end_time: 50
989 - start_size: 200
990";
991 let mut graph = GraphHolder::new();
992 assert_eq!(graph.init_with_yaml(0.0, yaml), 0);
993 let mut status = 0;
994 let demes_graph =
995 unsafe { forward_graph_get_demes_graph(graph.as_ptr(), &mut status) } as *const c_char;
996 assert!(!demes_graph.is_null());
997 let mut new_graph = GraphHolder::new();
998 let temp = unsafe { yaml_to_owned(demes_graph) }.unwrap();
999 assert_eq!(new_graph.init_with_yaml(0.0, &temp), 0);
1000 unsafe { libc::free(demes_graph as *mut libc::c_void) };
1001 assert_eq!(
1002 unsafe { &(*graph.graph) }
1003 .graph
1004 .as_ref()
1005 .unwrap()
1006 .demes_graph(),
1007 unsafe { &(*new_graph.graph) }
1008 .graph
1009 .as_ref()
1010 .unwrap()
1011 .demes_graph()
1012 )
1013 }
1014
1015 #[test]
1016 fn test_zero_length_model() {
1017 let yaml = "
1018time_units: generations
1019demes:
1020 - name: A
1021 epochs:
1022 - start_size: 100
1023";
1024 let mut graph = GraphHolder::new();
1025 assert_eq!(graph.init_with_yaml(0.0, yaml), 0);
1026 assert!(!unsafe { forward_graph_is_error_state(graph.as_ptr()) });
1027 assert_eq!(
1028 unsafe { forward_graph_initialize_time_iteration(graph.as_mut_ptr()) },
1029 0,
1030 );
1031 let mut ptime: *const f64;
1032 let mut status: i32 = -1;
1033 ptime = unsafe { forward_graph_iterate_time(graph.as_mut_ptr(), &mut status) };
1034 let mut ngens = 0;
1035 while !ptime.is_null() {
1036 assert_eq!(status, 0);
1037 ngens += 1;
1038 ptime = unsafe { forward_graph_iterate_time(graph.as_mut_ptr(), &mut status) };
1039 }
1040 assert_eq!(ngens, 1);
1045
1046 assert_eq!(
1050 unsafe { forward_graph_update_state(0.0, graph.as_mut_ptr()) },
1051 0
1052 );
1053 assert_eq!(
1054 unsafe { forward_graph_initialize_time_iteration(graph.as_mut_ptr()) },
1055 0,
1056 );
1057 ngens = 0;
1058
1059 let _ptime = unsafe { forward_graph_iterate_time(graph.as_mut_ptr(), &mut status) };
1061
1062 while unsafe { forward_graph_any_extant_offspring_demes(graph.as_ptr(), &mut status) } {
1064 ngens += 1;
1065 let _ptime = unsafe { forward_graph_iterate_time(graph.as_mut_ptr(), &mut status) };
1066 unsafe { forward_graph_update_state(*_ptime, graph.as_mut_ptr()) };
1067 }
1068 assert_eq!(ngens, 0);
1069 }
1070
1071 #[test]
1072 fn test_iteration_with_burnin() {
1073 let yaml = "
1074time_units: generations
1075demes:
1076 - name: A
1077 epochs:
1078 - start_size: 100
1079";
1080 for start_time in [0.0, 5.0, 10.0] {
1081 let mut graph = GraphHolder::new();
1082 assert_eq!(graph.init_with_yaml(10.0, yaml), 0);
1083 assert!(!unsafe { forward_graph_is_error_state(graph.as_ptr()) });
1084 let mut status: i32 = 0;
1085 let mut ngens = 0;
1086
1087 assert_eq!(
1090 unsafe { forward_graph_update_state(start_time, graph.as_mut_ptr()) },
1091 0
1092 );
1093
1094 assert_eq!(
1096 unsafe { forward_graph_initialize_time_iteration(graph.as_mut_ptr()) },
1097 0,
1098 );
1099 assert_eq!(
1100 unsafe { forward_graph_model_end_time(&mut status, graph.as_ptr()) },
1101 11.0
1102 );
1103 let _ptime = unsafe { forward_graph_iterate_time(graph.as_mut_ptr(), &mut status) };
1105 println!("ptime starts at {}", unsafe { *_ptime });
1106 assert_eq!(status, 0);
1107
1108 while unsafe { forward_graph_any_extant_offspring_demes(graph.as_ptr(), &mut status) } {
1112 assert_eq!(status, 0);
1113
1114 assert!(!unsafe {
1115 forward_graph_parental_deme_sizes(graph.as_ptr(), &mut status).is_null()
1116 });
1117 assert!(!unsafe {
1118 forward_graph_offspring_deme_sizes(graph.as_ptr(), &mut status).is_null()
1119 });
1120
1121 let _ptime = unsafe { forward_graph_iterate_time(graph.as_mut_ptr(), &mut status) };
1123 assert_eq!(
1125 unsafe { forward_graph_update_state(*_ptime, graph.as_mut_ptr()) },
1126 0
1127 );
1128 ngens += 1;
1129 }
1130 assert_eq!(ngens, (10.0 - start_time) as i32);
1131 }
1132 }
1133
1134 #[test]
1135 fn test_model_with_bad_time_rounding() {
1136 let yaml = "
1137time_units: generations
1138demes:
1139- name: bad
1140 epochs:
1141 - {end_time: 1.5, start_size: 1}
1142 - {end_time: 0.4, start_size: 2}
1143 - {end_time: 0, start_size: 3}
1144";
1145 let mut graph = GraphHolder::new();
1146 assert_eq!(graph.init_with_yaml(10.0, yaml), 0);
1147 let x = graph.as_ptr();
1148 assert!(unsafe { forward_graph_is_error_state(x) });
1149 }
1150
1151 #[test]
1152 fn test_non_integer_sizes_with_and_without_rounding() {
1153 let yaml = "
1154time_units: generations
1155demes:
1156- name: deme1
1157 start_time: .inf
1158 epochs:
1159 - {end_size: 99.99000049998334, end_time: 8000.0, start_size: 99.99000049998334}
1160 - {end_size: 100.0, end_time: 4000.0, start_size: 99.99000049998334}
1161 - {end_size: 100, end_time: 0, start_size: 100.0}
1162migrations: []
1163";
1164 {
1165 let mut graph = GraphHolder::new();
1166 assert_eq!(graph.init_with_yaml(10.0, yaml), 0);
1167 let x = graph.as_ptr();
1168 assert!(unsafe { forward_graph_is_error_state(x) });
1169 }
1170 {
1171 let mut graph = GraphHolder::new();
1172 assert_eq!(graph.init_with_yaml_round_epoch_sizes(10.0, yaml), 0);
1173 let x = graph.as_ptr();
1174 assert!(!unsafe { forward_graph_is_error_state(x) });
1175 }
1176 }
1177}