Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hackathon fall 2023 #39

Merged
merged 31 commits into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
8220c39
SIDARTHE model support
danbryce Oct 30, 2023
7ffb815
halfar example, bug fixes
danbryce Nov 1, 2023
cca8210
update outputs in notebook
danbryce Nov 1, 2023
c99adcc
updates for halfar
danbryce Nov 1, 2023
778b1d7
demo'd version of halfar model
danbryce Nov 2, 2023
1961b3d
fixing tests
danbryce Nov 2, 2023
14b11cb
add funman command line
danbryce Nov 3, 2023
a1a7592
fix tests
danbryce Nov 3, 2023
d71edd0
separate user constraints
danbryce Nov 3, 2023
d613005
change save_smtlib config to path
danbryce Nov 3, 2023
97410a4
demo updates
danbryce Nov 3, 2023
e0190a3
fix tests
danbryce Nov 3, 2023
8fc3e1c
hackathon demos
danbryce Nov 3, 2023
d1668bc
add depth-first search to box search
danbryce Nov 7, 2023
5bb3c7d
ignores
danbryce Nov 7, 2023
ad1a6ec
Merge remote-tracking branch 'origin/hackathon_fall_2023' into hackat…
danbryce Nov 7, 2023
2477075
halfar request
danbryce Nov 10, 2023
e38ac99
use normalized widths more carefully.
danbryce Nov 10, 2023
d1e890a
Merge remote-tracking branch 'origin/hackathon_fall_2023' into hackat…
danbryce Nov 10, 2023
4fb1c5f
fix test
danbryce Nov 10, 2023
1c95da1
small improvements to run
danbryce Nov 28, 2023
d662b74
Merge branch 'hackathon_fall_2023' of https://github.com/siftech/funm…
danbryce Nov 29, 2023
aa3dd13
avoid plotting parameters against self
danbryce Dec 1, 2023
e90adad
support absolute value
danbryce Dec 1, 2023
788dfa0
refine plotting
danbryce Dec 1, 2023
3a430be
logging
danbryce Dec 1, 2023
aaa0c0d
update unsat core parsing
danbryce Dec 1, 2023
3b12978
cleanup halfar generator
danbryce Dec 1, 2023
5a7d5db
move halfar examples
danbryce Dec 1, 2023
a7f324c
fix core parsing and handling abs, use pysmt fork
danbryce Dec 1, 2023
0cc9abc
fix tests
danbryce Dec 1, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -315,3 +315,4 @@ gmon.out
unsat.core
.gitignore
core.dimacs
notebooks/saved-results/out
298 changes: 298 additions & 0 deletions auxiliary_packages/funman_demo/src/funman_demo/generators/halfar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
"""
This script will generate instances of the Halfar ice model as Petrinet AMR models. The options control the number of discretization points.
"""

import argparse
import os
from decimal import Decimal
from typing import Dict, List, Tuple

from pydantic import AnyUrl, BaseModel

from funman.model.generated_models.petrinet import (
Distribution,
Header,
Initial,
Model,
Model1,
OdeSemantics,
Parameter,
Properties,
Rate,
Semantics,
State,
Time,
Transition,
Unit,
)
from funman.representation.interval import Interval


class HalfarModel(Model):
pass


class Direction:
Negative: str = "negative"
Positive: str = "positive"


class Coordinate(BaseModel):
"""
Coordinates are N-D points in Cartesian space, as denoted by the vector attribute. The neighbors are coordinates that are at the next point in each direction.
"""

vector: List[float]
id: str
neighbors: Dict[str, "Coordinate"] = {}


class HalfarGenerator:
"""
This generator class constructs the AMR instance. The coordinates are equally spaced across the range.
"""

range: Interval = Interval(lb=-2.0, ub=2.0)

def coordinates(self, args) -> List[Coordinate]:
"""
Generate coordinates for the range.
"""
coords = []
step_size = value = self.range.width() / Decimal(
int(args.num_discretization_points) - 1
)
for i in range(args.num_discretization_points):
value = self.range.lb + float(step_size * i)
coords.append(Coordinate(vector=[value], id=str(i)))
for i, coord in enumerate(coords):
coord.neighbors[Direction.Negative] = (
coords[i - 1] if i > 0 else None
)
coord.neighbors[Direction.Positive] = (
coords[i + 1] if i < len(coords) - 1 else None
)
return coords

def transition_expression(
self, n1_name: str, n2_name: str, negative=False
) -> str:
"""
Custom rate change
"""
prefix = "-1*" if negative else ""
gamma = "(2/(n+2))*A*(rho*g)**n"
return f"{prefix}{gamma}*(({n2_name}-{n1_name})**3)*({n1_name}**5)"

def neighbor_gradient(
self, coordinate: Coordinate, coordinates: List[Coordinate]
) -> Tuple[List[Transition], List[Rate]]:
"""
Find a triple of coordinates (n0, n1, n2) that are ordered so that dx = n2-n1 and dx = n1-n0 is positive
"""
if (
coordinate.neighbors[Direction.Positive]
and coordinate.neighbors[Direction.Negative]
):
n0 = coordinate.neighbors[Direction.Negative]
elif (
coordinate.neighbors[Direction.Positive]
and not coordinate.neighbors[Direction.Negative]
):
n0 = coordinate
elif (
not coordinate.neighbors[Direction.Positive]
and coordinate.neighbors[Direction.Negative]
):
n0 = coordinate.neighbors[Direction.Negative].neighbors[
Direction.Negative
]
else:
raise Exception(
"Cannot determine the gradient of a coordinate with no neighbors"
)
n1 = n0.neighbors[Direction.Positive]
n2 = n1.neighbors[Direction.Positive]

w_p_name = f"w_p_{coordinate.id}"
w_n_name = f"w_n_{coordinate.id}"
n0_name = f"h_{n0.id}"
n1_name = f"h_{n1.id}"
n2_name = f"h_{n2.id}"
h_name = f"h_{coordinate.id}"

# tp is the gradient wrt. n2, n1
tp = Transition(
id=w_p_name,
input=[n2_name, n1_name],
output=[h_name],
properties=Properties(name=w_p_name),
)

# tn is the gradient wrt. n1, n0
tn = Transition(
id=w_n_name,
input=[n1_name, n0_name],
output=[h_name],
properties=Properties(name=w_n_name),
)

transitions = [tp, tn]

tpr = Rate(
target=w_p_name,
expression=self.transition_expression(n1_name, n2_name),
)
tnr = Rate(
target=w_n_name,
expression=self.transition_expression(
n0_name, n1_name, negative=True
),
)

rates = [tpr, tnr]
return transitions, rates

def model(self, args) -> Tuple[Model1, Semantics]:
"""
Generate the AMR Model
"""
coordinates = self.coordinates(args)

# Create a height variable at each coordinate
states = [
State(id=f"h_{i}", name=f"h_{i}", description=f"height at {i}")
for i in range(len(coordinates))
]

transitions = []
rates = []
for coordinate in coordinates[1:-1]:
coord_transitions, trans_rates = self.neighbor_gradient(
coordinate, coordinates
)
transitions += coord_transitions
rates += trans_rates

time = Time(
id="t",
units=Unit(expression="day", expression_mathml="<ci>day</ci>"),
)

initials = [
Initial(
target=f"h_{c.id}",
expression=(
"0.0"
if (c == coordinates[0] or c == coordinates[-1])
else f"{1.0/(1.0+abs(c.vector[0]))}"
),
)
for c in coordinates
]

parameters = [
Parameter(
id="n",
value=3.0,
distribution=Distribution(
type="StandardUniform1",
parameters={"minimum": 3.0, "maximum": 3.0},
),
),
Parameter(
id="rho",
value=910.0,
distribution=Distribution(
type="StandardUniform1",
parameters={"minimum": 910.0, "maximum": 910.0},
),
),
Parameter(
id="g",
value=9.8,
distribution=Distribution(
type="StandardUniform1",
parameters={"minimum": 9.8, "maximum": 9.8},
),
),
Parameter(
id="A",
value=1e-16,
distribution=Distribution(
type="StandardUniform1",
parameters={"minimum": 1e-20, "maximum": 1e-12},
),
),
]

return Model1(states=states, transitions=transitions), Semantics(
ode=OdeSemantics(
rates=rates,
initials=initials,
parameters=parameters,
time=time,
)
)


def get_args():
parser = argparse.ArgumentParser()
# parser.add_argument(
# "-d",
# "--dimensions",
# default=1,
# type=int,
# help=f"Number of spatial dimensions",
# )
parser.add_argument(
"-p",
"--num-discretization-points",
default=5,
type=int,
help=f"Number of discretization points",
)
parser.add_argument(
"-o",
"--outfile",
default="halfar.json",
help=f"Output filename",
)
return parser.parse_args()


def header():
return Header(
name="Halfar Model",
schema_=AnyUrl(
"https://raw.githubusercontent.com/DARPA-ASKEM/Model-Representations/petrinet_v0.1/petrinet/petrinet_schema.json"
),
schema_name="petrinet",
description="Halfar as Petrinet model created by Dan Bryce and Drisana Mosiphir",
model_version="0.1",
)


def main():
args = get_args()

assert (
args.num_discretization_points > 2
), "Need to have use at least 3 discretization points to properly define the gradients."

generator = HalfarGenerator()
model, semantics = generator.model(args)
halfar_model = HalfarModel(
header=header(),
model=model,
semantics=semantics,
)
j = halfar_model.model_dump_json(indent=4)

with open(args.outfile, "w") as f:
print(f"Writing {os.path.join(os.getcwd(), args.outfile)}")
f.write(j)


if __name__ == "__main__":
main()
Loading