1
0
mirror of https://github.com/ellmau/adf-obdd.git synced 2025-12-20 09:39:38 +01:00

Support more options and multiple models

This commit is contained in:
monsterkrampe 2022-09-02 15:03:07 +02:00
parent e88e6bbfa1
commit 07537c6d25
No known key found for this signature in database
GPG Key ID: B8ADC1F5A5CE5057
3 changed files with 138 additions and 56 deletions

View File

@ -2,7 +2,21 @@ import * as React from 'react';
import { ThemeProvider, createTheme } from '@mui/material/styles'; import { ThemeProvider, createTheme } from '@mui/material/styles';
import { import {
Backdrop, Button, CircularProgress, CssBaseline, Container, Link, Paper, Typography, TextField, Backdrop,
Button,
CircularProgress,
CssBaseline,
Container,
FormControl,
FormControlLabel,
FormLabel,
Link,
Pagination,
Paper,
Radio,
RadioGroup,
Typography,
TextField,
} from '@mui/material'; } from '@mui/material';
import Graph from './graph.tsx'; import Graph from './graph.tsx';
@ -35,18 +49,27 @@ ac(10,and(neg(2),6)).
ac(1,and(neg(7),2)). ac(1,and(neg(7),2)).
ac(6,neg(7)).ac(2,and(neg(9),neg(6))).`; ac(6,neg(7)).ac(2,and(neg(9),neg(6))).`;
enum Parsing {
Naive = 'Naive',
Hybrid = 'Hybrid',
}
enum Strategy { enum Strategy {
ParseOnly = 'ParseOnly', ParseOnly = 'ParseOnly',
Ground = 'Ground', Ground = 'Ground',
FirstComplete = 'FirstComplete', Complete = 'Complete',
FirstStable = 'FirstStable', Stable = 'Stable',
FirstStableNogood = 'FirstStableNogood', StableCountingA = 'StableCountingA',
StableCountingB = 'StableCountingB',
StableNogood = 'StableNogood',
} }
function App() { function App() {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [code, setCode] = useState(placeholder); const [code, setCode] = useState(placeholder);
const [graph, setGraph] = useState(); const [parsing, setParsing] = useState(Parsing.Naive);
const [graphs, setGraphs] = useState();
const [graphIndex, setGraphIndex] = useState(0);
const submitHandler = useCallback( const submitHandler = useCallback(
(strategy: Strategy) => { (strategy: Strategy) => {
@ -57,18 +80,19 @@ function App() {
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ code, strategy }), body: JSON.stringify({ code, strategy, parsing }),
}) })
.then((res) => res.json()) .then((res) => res.json())
.then((data) => setGraph(data)) .then((data) => {
setGraphs(data);
setGraphIndex(0);
})
.finally(() => setLoading(false)); .finally(() => setLoading(false));
// TODO: error handling // TODO: error handling
}, },
[code], [code, parsing],
); );
console.log(graph);
return ( return (
<ThemeProvider theme={darkTheme}> <ThemeProvider theme={darkTheme}>
<CssBaseline /> <CssBaseline />
@ -97,24 +121,56 @@ function App() {
onChange={(event) => { setCode(event.target.value); }} onChange={(event) => { setCode(event.target.value); }}
/> />
</Container> </Container>
<Container> <Container sx={{ marginTop: 2, marginBottom: 2 }}>
<FormControl>
<FormLabel id="parsing-radio-group">Parsing Strategy</FormLabel>
<RadioGroup
row
aria-labelledby="parsing-radio-group"
name="parsing"
value={parsing}
onChange={(e) => setParsing((e.target as HTMLInputElement).value)}
>
<FormControlLabel value={Parsing.Naive} control={<Radio />} label="Naive" />
<FormControlLabel value={Parsing.Hybrid} control={<Radio />} label="Hybrid" />
</RadioGroup>
</FormControl>
<br />
<br />
<Button variant="outlined" onClick={() => submitHandler(Strategy.ParseOnly)}>Parse only</Button> <Button variant="outlined" onClick={() => submitHandler(Strategy.ParseOnly)}>Parse only</Button>
{' '} {' '}
<Button variant="outlined" onClick={() => submitHandler(Strategy.Ground)}>Grounded Model</Button> <Button variant="outlined" onClick={() => submitHandler(Strategy.Ground)}>Grounded Model</Button>
{' '} {' '}
<Button variant="outlined" onClick={() => submitHandler(Strategy.FirstComplete)}>(First) Complete Model</Button> <Button variant="outlined" onClick={() => submitHandler(Strategy.Complete)}>Complete Models</Button>
{' '} {' '}
<Button variant="outlined" onClick={() => submitHandler(Strategy.FirstStable)}>(First) Stable Model</Button> <Button variant="outlined" onClick={() => submitHandler(Strategy.Stable)}>Stable Models (naive heuristics)</Button>
{' '} {' '}
<Button variant="outlined" onClick={() => submitHandler(Strategy.FirstStableNogood)}>(First) Stable Model using nogoods</Button> <Button disabled={parsing !== Parsing.Hybrid} variant="outlined" onClick={() => submitHandler(Strategy.StableCountingA)}>Stable Models (counting heuristic A)</Button>
{' '}
<Button disabled={parsing !== Parsing.Hybrid} variant="outlined" onClick={() => submitHandler(Strategy.StableCountingB)}>Stable Models (counting heuristic B)</Button>
{' '}
<Button variant="outlined" onClick={() => submitHandler(Strategy.StableNogood)}>Stable Models using nogoods (Simple Heuristic)</Button>
</Container> </Container>
{graph {graphs
&& ( && (
<Container> <Container sx={{ marginTop: 4, marginBottom: 4 }}>
<Paper elevation={3} square sx={{ marginTop: 4, marginBottom: 4 }}> {graphs.length > 1
<Graph graph={graph} /> && (
</Paper> <>
Models:
<br />
<Pagination variant="outlined" shape="rounded" count={graphs.length} page={graphIndex + 1} onChange={(e, value) => setGraphIndex(value - 1)} />
</>
)}
{graphs.length > 0
&& (
<Paper elevation={3} square sx={{ marginTop: 4, marginBottom: 4 }}>
<Graph graph={graphs[graphIndex]} />
</Paper>
)}
{graphs.length === 0
&& <>No models!</>}
</Container> </Container>
)} )}
</main> </main>

View File

@ -4,8 +4,6 @@ import G6 from '@antv/g6';
G6.registerNode('nodeWithFlag', { G6.registerNode('nodeWithFlag', {
draw(cfg, group) { draw(cfg, group) {
console.log('cfg', cfg);
const mainWidth = Math.max(30, 5 * cfg.mainLabel.length + 10); const mainWidth = Math.max(30, 5 * cfg.mainLabel.length + 10);
const mainHeight = 30; const mainHeight = 30;
@ -14,10 +12,9 @@ G6.registerNode('nodeWithFlag', {
width: mainWidth, width: mainWidth,
height: mainHeight, height: mainHeight,
radius: 2, radius: 2,
fill: cfg.fill || 'white', fill: 'white',
stroke: 'black', stroke: 'black',
lineWidth: cfg.lineWidth, cursor: 'pointer',
opacity: cfg.opacity,
}, },
name: 'rectMainLabel', name: 'rectMainLabel',
draggable: true, draggable: true,
@ -32,6 +29,7 @@ G6.registerNode('nodeWithFlag', {
text: cfg.mainLabel, text: cfg.mainLabel,
fill: '#212121', fill: '#212121',
fontFamily: 'Roboto', fontFamily: 'Roboto',
cursor: 'pointer',
}, },
// must be assigned in G6 3.3 and later versions. it can be any value you want // must be assigned in G6 3.3 and later versions. it can be any value you want
name: 'textMailLabel', name: 'textMailLabel',
@ -55,7 +53,7 @@ G6.registerNode('nodeWithFlag', {
radius: 1, radius: 1,
fill: '#4caf50', fill: '#4caf50',
stroke: '#1b5e20', stroke: '#1b5e20',
opacity: cfg.opacity, cursor: 'pointer',
}, },
name: 'rectMainLabel', name: 'rectMainLabel',
draggable: true, draggable: true,
@ -71,6 +69,7 @@ G6.registerNode('nodeWithFlag', {
fill: '#212121', fill: '#212121',
fontFamily: 'Roboto', fontFamily: 'Roboto',
fontSize: 10, fontSize: 10,
cursor: 'pointer',
}, },
// must be assigned in G6 3.3 and later versions. it can be any value you want // must be assigned in G6 3.3 and later versions. it can be any value you want
name: 'textMailLabel', name: 'textMailLabel',
@ -97,29 +96,36 @@ G6.registerNode('nodeWithFlag', {
// }, // },
setState(name, value, item) { setState(name, value, item) {
const group = item.getContainer(); const group = item.getContainer();
const shape = group.get('children')[0]; // Find the first graphics shape of the node. It is determined by the order of being added const mainShape = group.get('children')[0]; // Find the first graphics shape of the node. It is determined by the order of being added
const subShape = group.get('children')[2];
if (name === 'hover') { if (name === 'hover') {
if (value) { if (value) {
shape.attr('fill', 'lightsteelblue'); mainShape.attr('fill', 'lightsteelblue');
} else { } else {
shape.attr('fill', 'white'); mainShape.attr('fill', 'white');
} }
} }
if (name === 'highlight') { if (name === 'highlight') {
if (value) { if (value) {
shape.attr('lineWidth', 3); mainShape.attr('lineWidth', 3);
} else { } else {
shape.attr('lineWidth', 1); mainShape.attr('lineWidth', 1);
} }
} }
if (name === 'lowlight') { if (name === 'lowlight') {
if (value) { if (value) {
shape.attr('opacity', 0.3); mainShape.attr('opacity', 0.3);
if (subShape) {
subShape.attr('opacity', 0.3);
}
} else { } else {
shape.attr('opacity', 1); mainShape.attr('opacity', 1);
if (subShape) {
subShape.attr('opacity', 1);
}
} }
} }
}, },
@ -282,9 +288,6 @@ function Graph(props: Props) {
.map(([source, target]) => `HI_${source}_${target}`), .map(([source, target]) => `HI_${source}_${target}`),
); );
console.log(relevantNodeIds);
console.log(relevantEdgeIds);
relevantNodeIds relevantNodeIds
.forEach((id) => { .forEach((id) => {
graph.setItemState(id, 'lowlight', false); graph.setItemState(id, 'lowlight', false);
@ -347,8 +350,8 @@ function Graph(props: Props) {
return ( return (
<> <>
<div ref={ref} style={{ overflow: 'hidden' }} /> <div ref={ref} style={{ overflow: 'hidden' }} />
<div> <div style={{ padding: 4 }}>
<span style={{ color: '#ed6c02' }}>lo edge (condition is false)</span> <span style={{ color: '#ed6c02', marginRight: 8 }}>lo edge (condition is false)</span>
{' '} {' '}
<span style={{ color: '#1976d2' }}>hi edge (condition is true)</span> <span style={{ color: '#1976d2' }}>hi edge (condition is true)</span>
</div> </div>

View File

@ -8,20 +8,30 @@ use actix_cors::Cors;
use actix_web::http; use actix_web::http;
use adf_bdd::adf::{Adf, DoubleLabeledGraph}; use adf_bdd::adf::{Adf, DoubleLabeledGraph};
use adf_bdd::adfbiodivine::Adf as BdAdf;
use adf_bdd::parser::AdfParser; use adf_bdd::parser::AdfParser;
#[derive(Deserialize)]
enum Parsing {
Naive,
Hybrid,
}
#[derive(Deserialize)] #[derive(Deserialize)]
enum Strategy { enum Strategy {
ParseOnly, ParseOnly,
Ground, Ground,
FirstComplete, Complete,
FirstStable, Stable,
FirstStableNogood, StableCountingA,
StableCountingB,
StableNogood,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
struct SolveReqBody { struct SolveReqBody {
code: String, code: String,
parsing: Parsing,
strategy: Strategy, strategy: Strategy,
} }
@ -33,6 +43,7 @@ struct SolveResBody {
#[post("/solve")] #[post("/solve")]
async fn solve(req_body: web::Json<SolveReqBody>) -> impl Responder { async fn solve(req_body: web::Json<SolveReqBody>) -> impl Responder {
let input = &req_body.code; let input = &req_body.code;
let parsing = &req_body.parsing;
let strategy = &req_body.strategy; let strategy = &req_body.strategy;
let parser = AdfParser::default(); let parser = AdfParser::default();
@ -43,29 +54,41 @@ async fn solve(req_body: web::Json<SolveReqBody>) -> impl Responder {
panic!("Parsing failed, see log for further details") panic!("Parsing failed, see log for further details")
} }
} }
log::info!("[Done] parsing");
let mut adf = Adf::from_parser(&parser); let mut adf = match parsing {
Parsing::Naive => Adf::from_parser(&parser),
Parsing::Hybrid => {
let bd_adf = BdAdf::from_parser(&parser);
log::info!("[Start] translate into naive representation");
let naive_adf = bd_adf.hybrid_step();
log::info!("[Done] translate into naive representation");
naive_adf
}
};
log::debug!("{:?}", adf); log::debug!("{:?}", adf);
let ac = match strategy { let acs = match strategy {
Strategy::ParseOnly => None, Strategy::ParseOnly => vec![None],
Strategy::Ground => Some(adf.grounded()), Strategy::Ground => vec![Some(adf.grounded())],
// TODO: error handling if no such model exists! Strategy::Complete => adf.complete().map(Some).collect(),
Strategy::FirstComplete => Some(adf.complete().next().unwrap()), Strategy::Stable => adf.stable().map(Some).collect(),
// TODO: error handling if no such model exists! // TODO: INPUT VALIDATION: only allow this for hybrid parsing
Strategy::FirstStable => Some(adf.stable().next().unwrap()), Strategy::StableCountingA => adf.stable_count_optimisation_heu_a().map(Some).collect(),
// TODO: error handling if no such model exists! // TODO: INPUT VALIDATION: only allow this for hybrid parsing
Strategy::StableCountingB => adf.stable_count_optimisation_heu_b().map(Some).collect(),
// TODO: support more than just default heuristics // TODO: support more than just default heuristics
Strategy::FirstStableNogood => Some( Strategy::StableNogood => adf
adf.stable_nogood(adf_bdd::adf::heuristics::Heuristic::default()) .stable_nogood(adf_bdd::adf::heuristics::Heuristic::default())
.next() .map(Some)
.unwrap(), .collect(),
),
}; };
let dto = adf.into_double_labeled_graph(ac.as_ref()); let dto: Vec<DoubleLabeledGraph> = acs
.iter()
.map(|ac| adf.into_double_labeled_graph(ac.as_ref()))
.collect();
web::Json(dto) web::Json(dto)
} }