Skip to content

Commit e585f69

Browse files
committed
better output
1 parent cf790ea commit e585f69

File tree

5 files changed

+82
-128
lines changed

5 files changed

+82
-128
lines changed

examples/bug12.tikz

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
%% Machine generated by https://finsm.io
2-
<<<<<<< HEAD
3-
%% 2025-4-2-5:38:3
4-
=======
52
%% 2025-3-26-19:34:5
6-
>>>>>>> c2d2e77 (lint)
73
%% include in preamble:
84
%% \usepackage{tikz}
95
%% \usetikzlibrary{automata,positioning,arrows}
106
\begin{center}
117
\begin{tikzpicture}[]
12-
<<<<<<< HEAD
138
\node[initial,thick,state] at (-5.4,1) (dfe6a702) {$i$};
149
\node[thick,state] at (-1.625,2.925) (846003ad) {$1$};
1510
\node[thick,state] at (-1.75,-0.75) (500c3f6e) {$2$};
@@ -37,32 +32,3 @@
3732
;
3833
\end{tikzpicture}
3934
\end{center}
40-
=======
41-
\node[initial,thick,state] at (-5.4,1) (e6bc1340) {$i$};
42-
\node[thick,state] at (-1.625,2.925) (02aae376) {$1$};
43-
\node[thick,state] at (-1.75,-0.75) (b8a21493) {$2$};
44-
\node[thick,accepting,state] at (4.75,1.25) (892cfee8) {$f$};
45-
\node[thick,state] at (1.25,4) (d6522f64) {$l1$};
46-
\node[thick,state] at (1.25,1.75) (6c95f26d) {$l2$};
47-
\node[thick,state] at (1.5,0) (34b30707) {$l3$};
48-
\node[thick,state] at (1.5,-1.75) (5d491a74) {$l4$};
49-
\path[->, thick, >=stealth]
50-
(e6bc1340) edge [left,in = -160, out = 34] node {$b$} (02aae376)
51-
(e6bc1340) edge [left,in = 157, out = -28] node {$b$} (b8a21493)
52-
(02aae376) edge [left,in = 111, out = -118] node {$a$} (b8a21493)
53-
(02aae376) edge [loop,min distance = 1.25cm,above,in = 121, out = 59] node {$a$} (02aae376)
54-
(02aae376) edge [right,in = -159, out = 21] node {$b$} (d6522f64)
55-
(02aae376) edge [left,in = 158, out = -22] node {$b$} (6c95f26d)
56-
(b8a21493) edge [right,in = -59, out = 55] node {$a$} (02aae376)
57-
(b8a21493) edge [loop,min distance = 1.25cm,below,in = -59, out = -121] node {$a$} (b8a21493)
58-
(b8a21493) edge [right,in = -167, out = 13] node {$b$} (34b30707)
59-
(b8a21493) edge [left,in = 163, out = -17] node {$b$} (5d491a74)
60-
(892cfee8) edge [loop,min distance = 1.25cm,above,in = 121, out = 59] node {$b$} (892cfee8)
61-
(d6522f64) edge [left,in = 142, out = -38] node {$c,d$} (892cfee8)
62-
(6c95f26d) edge [left,in = 172, out = -8] node {$e,f$} (892cfee8)
63-
(34b30707) edge [right,in = -159, out = 21] node {$c,f$} (892cfee8)
64-
(5d491a74) edge [right,in = -137, out = 43] node {$d,e$} (892cfee8)
65-
;
66-
\end{tikzpicture}
67-
\end{center}
68-
>>>>>>> c2d2e77 (lint)

src/main.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,19 @@ fn main() {
104104
solver::SolverOutput::Strategy => println!("\nMaximal winning strategy;\n{}", solution),
105105
solver::SolverOutput::YesNo => {
106106
println!("\nSolution\n{}", solution);
107-
println!("\nWinning strategy;\n{}", solution.winning_strategy);
107+
if solution.is_controllable {
108+
println!(
109+
"\nStrategy winning from the initial ideal (might not be maximal)\n{}",
110+
solution.winning_strategy
111+
);
112+
}
108113
}
109114
}
110115

111116
// only if the answer was positive, format the winning strategy
112117
let output_strategy = match args.solver_output {
113118
solver::SolverOutput::Strategy => true,
114-
solver::SolverOutput::YesNo => solution.result.unwrap_or(false),
119+
solver::SolverOutput::YesNo => solution.is_controllable,
115120
};
116121
if output_strategy {
117122
// create a writer were we later print the output.
@@ -174,7 +179,7 @@ mod tests {
174179
let nfa = nfa::Nfa::from_tikz(EXAMPLE1);
175180
let solution = solver::solve(&nfa, &solver::SolverOutput::YesNo);
176181
print!("{}", solution);
177-
assert!(!solution.result.unwrap_or(false));
182+
assert!(!solution.is_controllable);
178183
assert_eq!(solution.winning_strategy.iter().count(), 2);
179184
let ideala = solution
180185
.winning_strategy
@@ -203,7 +208,7 @@ mod tests {
203208
let nfa = nfa::Nfa::from_tikz(EXAMPLE1_COMPLETE);
204209
let solution = solver::solve(&nfa, &solver::SolverOutput::YesNo);
205210
print!("{}", solution);
206-
assert!(!solution.result.unwrap_or(false));
211+
assert!(!solution.is_controllable);
207212
assert_eq!(solution.winning_strategy.iter().count(), 2);
208213
let ideala = solution
209214
.winning_strategy
@@ -229,7 +234,7 @@ mod tests {
229234
let nfa = nfa::Nfa::from_tikz(EXAMPLE2);
230235
let solution = solver::solve(&nfa, &solver::SolverOutput::Strategy);
231236
print!("{}", solution);
232-
assert!(!solution.result.unwrap_or(false));
237+
assert!(!solution.is_controllable);
233238
assert_eq!(solution.winning_strategy.iter().count(), 4);
234239
let ideala = solution
235240
.winning_strategy
@@ -247,7 +252,7 @@ mod tests {
247252
let mut nfa = nfa::Nfa::from_tikz(EXAMPLE2);
248253
nfa.sort(&nfa::StateOrdering::Alphabetical);
249254
let solution = solver::solve(&nfa, &solver::SolverOutput::Strategy);
250-
assert!(!solution.result.unwrap_or(false));
255+
assert!(!solution.is_controllable);
251256
assert_eq!(solution.winning_strategy.iter().count(), 4);
252257
let ideala = solution
253258
.winning_strategy
@@ -265,7 +270,7 @@ mod tests {
265270
let mut nfa = nfa::Nfa::from_tikz(EXAMPLE2);
266271
nfa.sort(&nfa::StateOrdering::Topological);
267272
let solution = solver::solve(&nfa, &solver::SolverOutput::Strategy);
268-
assert!(!solution.result.unwrap_or(false));
273+
assert!(!solution.is_controllable);
269274
assert_eq!(solution.winning_strategy.iter().count(), 4);
270275
let ideala = solution
271276
.winning_strategy

src/solution.rs

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use tera::{Context, Tera};
66
/// A solution to the population control problem.
77
pub struct Solution {
88
pub nfa: Nfa,
9-
pub result: Option<bool>,
9+
pub is_controllable: bool,
1010
pub winning_strategy: Strategy,
1111
}
1212

@@ -30,12 +30,8 @@ impl Solution {
3030
context.insert("accepting", &self.nfa.accepting_states_str());
3131
context.insert("transitions", &self.nfa.transitions_str());
3232

33-
let answer = match self.result {
34-
None => "",
35-
Some(true) => "YES (controllable)",
36-
Some(false) => "NO (uncontrollable)",
37-
};
38-
context.insert("answer", answer);
33+
let answer = self.to_string();
34+
context.insert("answer", &answer);
3935

4036
context.insert("strategy", &self.winning_strategy.to_string());
4137

@@ -51,10 +47,9 @@ impl Solution {
5147

5248
impl fmt::Display for Solution {
5349
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
54-
let answer = match self.result {
55-
None => self.winning_strategy.to_string(),
56-
Some(true) => "\tYES (controllable)".to_string(),
57-
Some(false) => "\tNO (uncontrollable)".to_string(),
50+
let answer = match self.is_controllable {
51+
true => "\tYES (controllable)".to_string(),
52+
false => "\tNO (uncontrollable)".to_string(),
5853
};
5954
writeln!(f, "Answer:\n{}", answer)
6055
}

src/solver.rs

Lines changed: 61 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -18,80 +18,50 @@ pub enum SolverOutput {
1818
}
1919

2020
pub fn solve(nfa: &nfa::Nfa, output: &SolverOutput) -> Solution {
21-
match output {
22-
SolverOutput::Strategy => compute_maximal_winning_strategy(nfa),
23-
SolverOutput::YesNo => compute_control_problem_solution(nfa),
24-
}
25-
}
26-
27-
fn compute_control_problem_solution(nfa: &nfa::Nfa) -> Solution {
2821
let dim = nfa.nb_states();
2922
let source = get_omega_sheep(
3023
dim,
3124
&nfa.initial_states().iter().cloned().collect::<Vec<_>>(),
3225
);
3326
let final_states = nfa.final_states();
34-
3527
let edges = nfa.get_edges();
36-
let mut strategy = Strategy::get_maximal_strategy(dim, &nfa.get_alphabet());
37-
38-
for maximal_finite_value in 1..dim as coef {
39-
let mut step = 1;
40-
loop {
41-
//convert strategy to flows
42-
info!(
43-
"Looking for a winning strategy using maximal finite_value {} step {}",
44-
maximal_finite_value, step
45-
);
46-
step += 1;
47-
48-
let changed = update_strategy(
49-
dim,
50-
&mut strategy,
51-
&final_states,
52-
&edges,
53-
maximal_finite_value,
54-
);
55-
let result = strategy.is_defined_on(&source);
56-
57-
if !changed || !result {
58-
break;
59-
}
28+
let letters = nfa.get_alphabet();
29+
let strategy = match output {
30+
SolverOutput::Strategy => {
31+
compute_maximal_winning_strategy(dim, &final_states, edges, &letters)
6032
}
61-
if strategy.is_defined_on(&source) {
62-
break;
33+
SolverOutput::YesNo => {
34+
compute_control_problem_solution(dim, &source, &final_states, edges, &letters)
6335
}
64-
}
36+
};
37+
let is_controllable = strategy.is_defined_on(&source);
6538
Solution {
6639
nfa: nfa.clone(),
67-
result: Some(strategy.is_defined_on(&source)),
40+
is_controllable,
6841
winning_strategy: strategy,
6942
}
7043
}
7144

72-
fn compute_maximal_winning_strategy(nfa: &nfa::Nfa) -> Solution {
73-
let dim = nfa.nb_states();
45+
fn compute_maximal_winning_strategy(
46+
dim: usize,
47+
final_states: &[usize],
48+
edges: HashMap<String, Graph>,
49+
letters: &[&str],
50+
) -> Strategy {
7451
let maximal_finite_value = dim as coef;
75-
let final_states = nfa.final_states();
7652

77-
let edges = nfa.get_edges();
78-
let mut strategy = Strategy::get_maximal_strategy(dim, &nfa.get_alphabet());
53+
let mut strategy = Strategy::get_maximal_strategy(dim, letters);
7954

8055
let mut step = 1;
8156
loop {
8257
//convert strategy to flows
83-
info!(
84-
"Computing the maximal winning strategy step {} states\n{}\ncurrent strategy\n{}",
85-
step,
86-
nfa.states_str(),
87-
strategy
88-
);
58+
info!("Computing the maximal winning strategy step {}", step);
8959
step += 1;
9060

9161
let changed = update_strategy(
9262
dim,
9363
&mut strategy,
94-
&final_states,
64+
final_states,
9565
&edges,
9666
maximal_finite_value,
9767
);
@@ -100,11 +70,46 @@ fn compute_maximal_winning_strategy(nfa: &nfa::Nfa) -> Solution {
10070
break;
10171
}
10272
}
103-
Solution {
104-
nfa: nfa.clone(),
105-
result: None,
106-
winning_strategy: strategy,
73+
strategy
74+
}
75+
76+
fn compute_control_problem_solution(
77+
dim: usize,
78+
source: &Sheep,
79+
final_states: &[usize],
80+
edges: HashMap<String, Graph>,
81+
letters: &[&str],
82+
) -> Strategy {
83+
let mut strategy = Strategy::get_maximal_strategy(dim, letters);
84+
85+
for maximal_finite_value in 1..dim as coef {
86+
let mut step = 1;
87+
loop {
88+
//convert strategy to flows
89+
info!(
90+
"Looking for a winning strategy using maximal finite_value {} step {}",
91+
maximal_finite_value, step
92+
);
93+
step += 1;
94+
95+
let changed = update_strategy(
96+
dim,
97+
&mut strategy,
98+
final_states,
99+
&edges,
100+
maximal_finite_value,
101+
);
102+
let result = strategy.is_defined_on(source);
103+
104+
if !changed || !result {
105+
break;
106+
}
107+
}
108+
if strategy.is_defined_on(source) {
109+
break;
110+
}
107111
}
112+
strategy
108113
}
109114

110115
fn update_strategy(
@@ -126,18 +131,7 @@ fn update_strategy(
126131
debug!("Computing winning ideal");
127132
let mut winning_ideal = semigroup.get_path_problem_solution(final_states);
128133
winning_ideal.insert(&final_ideal);
129-
//non-omega stay below dim
130-
/*
131-
debug!(
132-
"Winning ideal for the path problem before rounding down\n{}",
133-
winning_ideal
134-
);*/
135-
winning_ideal.round_down(maximal_finite_value, dim); //backed by the small constants theorem
136-
/*
137-
debug!(
138-
"Winning ideal for the path problem before minimize\n{}",
139-
winning_ideal
140-
);*/
134+
winning_ideal.round_down(maximal_finite_value, dim);
141135
winning_ideal.minimize();
142136
debug!("Winning ideal for the path problem:\n{}", winning_ideal);
143137
debug!("Restricting strategy");
@@ -279,7 +273,7 @@ mod tests {
279273
nfa.add_transition_by_index1(0, 2, 'a');
280274
nfa.add_transition_by_index1(1, 2, 'a');
281275
let solution = solve(&nfa, &SolverOutput::Strategy);
282-
assert!(!solution.result.unwrap_or(false));
276+
assert!(!solution.is_controllable);
283277
}
284278

285279
#[test]
@@ -293,7 +287,7 @@ mod tests {
293287
nfa.add_transition_by_index1(2, 2, 'a');
294288
let solution = solve(&nfa, &SolverOutput::Strategy);
295289
print!("{}", solution);
296-
assert!(!solution.result.unwrap_or(false));
290+
assert!(!solution.is_controllable);
297291
}
298292

299293
#[test]
@@ -324,6 +318,6 @@ mod tests {
324318

325319
let solution = solve(&nfa, &SolverOutput::YesNo);
326320
print!("{}", solution);
327-
assert!(solution.result.unwrap_or(false));
321+
assert!(solution.is_controllable);
328322
}
329323
}

src/strategy.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,7 @@ impl Strategy {
2626
}
2727

2828
pub fn is_defined_on(&self, source: &Sheep) -> bool {
29-
let answer = self.0.values().any(|ideal| ideal.contains(source));
30-
if answer {
31-
println!("is_defined_on {}", source);
32-
} else {
33-
println!("is_not_defined_on {}", source);
34-
}
35-
answer
29+
self.0.values().any(|ideal| ideal.contains(source))
3630
}
3731

3832
pub(crate) fn restrict_to(
@@ -56,12 +50,12 @@ impl Strategy {
5650
pub(crate) fn iter(&self) -> impl Iterator<Item = (&nfa::Letter, &Ideal)> {
5751
self.0.iter()
5852
}
59-
53+
6054
// create a CSV representation of this strategy.
6155
pub fn as_csv(&self) -> String {
6256
let mut lines: Vec<nfa::Letter> = Vec::new();
6357

64-
for (a,i) in &self.0 {
58+
for (a, i) in &self.0 {
6559
for s in i.as_csv() {
6660
let l = format!("{a},{s}");
6761
lines.push(l);

0 commit comments

Comments
 (0)