1use std::collections::HashMap;
4
5use crate::net::PetriNet;
6
7pub struct Builder {
9 net: PetriNet,
10 next_x: f64,
11 place_y: f64,
12 trans_y: f64,
13}
14
15impl Builder {
16 pub fn new() -> Self {
18 Self {
19 net: PetriNet::new(),
20 next_x: 100.0,
21 place_y: 100.0,
22 trans_y: 200.0,
23 }
24 }
25
26 pub fn place(mut self, label: &str, initial: f64) -> Self {
28 self.net.add_place(
29 label,
30 vec![initial],
31 vec![],
32 self.next_x,
33 self.place_y,
34 None,
35 );
36 self.next_x += 100.0;
37 self
38 }
39
40 pub fn place_with_capacity(mut self, label: &str, initial: f64, capacity: f64) -> Self {
42 self.net.add_place(
43 label,
44 vec![initial],
45 vec![capacity],
46 self.next_x,
47 self.place_y,
48 None,
49 );
50 self.next_x += 100.0;
51 self
52 }
53
54 pub fn transition(mut self, label: &str) -> Self {
56 self.net
57 .add_transition(label, "default", self.next_x, self.trans_y, None);
58 self.next_x += 100.0;
59 self
60 }
61
62 pub fn transition_with_role(mut self, label: &str, role: &str) -> Self {
64 self.net
65 .add_transition(label, role, self.next_x, self.trans_y, None);
66 self.next_x += 100.0;
67 self
68 }
69
70 pub fn arc(mut self, source: &str, target: &str, weight: f64) -> Self {
72 self.net
73 .add_arc(source, target, vec![weight], false);
74 self
75 }
76
77 pub fn inhibitor_arc(mut self, source: &str, target: &str, weight: f64) -> Self {
79 self.net
80 .add_arc(source, target, vec![weight], true);
81 self
82 }
83
84 pub fn flow(mut self, from_place: &str, transition: &str, to_place: &str, weight: f64) -> Self {
86 self.net
87 .add_arc(from_place, transition, vec![weight], false);
88 self.net
89 .add_arc(transition, to_place, vec![weight], false);
90 self
91 }
92
93 pub fn chain(mut self, initial_tokens: f64, elements: &[&str]) -> Self {
98 if elements.len() < 3 || elements.len() % 2 == 0 {
99 return self;
100 }
101
102 self = self.place(elements[0], initial_tokens);
103
104 let mut i = 1;
105 while i < elements.len() {
106 let trans = elements[i];
107 let next_place = elements[i + 1];
108
109 self = self.transition(trans);
110 self = self.place(next_place, 0.0);
111 self.net
112 .add_arc(elements[i - 1], trans, vec![1.0], false);
113 self.net
114 .add_arc(trans, next_place, vec![1.0], false);
115 i += 2;
116 }
117
118 self
119 }
120
121 pub fn sir(self, susceptible: f64, infected: f64, recovered: f64) -> Self {
123 self.place("S", susceptible)
124 .place("I", infected)
125 .place("R", recovered)
126 .transition("infect")
127 .transition("recover")
128 .arc("S", "infect", 1.0)
129 .arc("I", "infect", 1.0)
130 .arc("infect", "I", 2.0)
131 .arc("I", "recover", 1.0)
132 .arc("recover", "R", 1.0)
133 }
134
135 pub fn done(self) -> PetriNet {
137 self.net
138 }
139
140 pub fn with_rates(self, default_rate: f64) -> (PetriNet, HashMap<String, f64>) {
142 let mut rates = HashMap::new();
143 for label in self.net.transitions.keys() {
144 rates.insert(label.clone(), default_rate);
145 }
146 (self.net, rates)
147 }
148
149 pub fn with_custom_rates(
151 self,
152 rates: HashMap<String, f64>,
153 ) -> (PetriNet, HashMap<String, f64>) {
154 (self.net, rates)
155 }
156}
157
158impl Default for Builder {
159 fn default() -> Self {
160 Self::new()
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167
168 #[test]
169 fn test_basic_builder() {
170 let net = Builder::new()
171 .place("A", 10.0)
172 .place("B", 0.0)
173 .transition("t1")
174 .arc("A", "t1", 1.0)
175 .arc("t1", "B", 1.0)
176 .done();
177
178 assert_eq!(net.places.len(), 2);
179 assert_eq!(net.transitions.len(), 1);
180 assert_eq!(net.arcs.len(), 2);
181 assert_eq!(net.places["A"].token_count(), 10.0);
182 }
183
184 #[test]
185 fn test_sir_builder() {
186 let (net, rates) = PetriNet::build().sir(999.0, 1.0, 0.0).with_rates(1.0);
187
188 assert_eq!(net.places.len(), 3);
189 assert_eq!(net.transitions.len(), 2);
190 assert_eq!(net.arcs.len(), 5);
191 assert_eq!(net.places["S"].token_count(), 999.0);
192 assert_eq!(net.places["I"].token_count(), 1.0);
193 assert_eq!(net.places["R"].token_count(), 0.0);
194 assert_eq!(rates["infect"], 1.0);
195 assert_eq!(rates["recover"], 1.0);
196 }
197
198 #[test]
199 fn test_chain_builder() {
200 let net = Builder::new()
201 .chain(1.0, &["Received", "start", "Processing", "finish", "Complete"])
202 .done();
203
204 assert_eq!(net.places.len(), 3);
205 assert_eq!(net.transitions.len(), 2);
206 assert_eq!(net.places["Received"].token_count(), 1.0);
207 assert_eq!(net.places["Processing"].token_count(), 0.0);
208 assert_eq!(net.places["Complete"].token_count(), 0.0);
209 }
210
211 #[test]
212 fn test_with_rates() {
213 let (net, rates) = Builder::new()
214 .place("A", 10.0)
215 .transition("t1")
216 .transition("t2")
217 .arc("A", "t1", 1.0)
218 .with_rates(0.5);
219
220 assert_eq!(rates.len(), 2);
221 assert_eq!(rates["t1"], 0.5);
222 assert_eq!(rates["t2"], 0.5);
223 assert_eq!(net.places.len(), 1);
224 }
225
226 #[test]
227 fn test_flow_builder() {
228 let net = Builder::new()
229 .place("input", 5.0)
230 .place("output", 0.0)
231 .transition("process")
232 .flow("input", "process", "output", 1.0)
233 .done();
234
235 assert_eq!(net.arcs.len(), 2);
236 }
237}