From ba4170fd49219ea22f33a11069bcd0fbf2d5c89e Mon Sep 17 00:00:00 2001
From: "Hsieh, Chiao" <chsieh16@illinois.edu>
Date: Mon, 18 Apr 2022 14:31:49 -0500
Subject: [PATCH] Add sympy teacher

---
 gem_stanley_teacher.py | 129 +++++++++++++++++++++++++++++++++++------
 1 file changed, 110 insertions(+), 19 deletions(-)

diff --git a/gem_stanley_teacher.py b/gem_stanley_teacher.py
index 6b43b2c..59c8ef0 100644
--- a/gem_stanley_teacher.py
+++ b/gem_stanley_teacher.py
@@ -3,8 +3,9 @@ import dreal
 import gurobipy as gp
 from gurobipy import GRB
 import numpy as np
+import sympy
 
-from teacher_base import GurobiTeacherBase, DRealTeacherBase
+from teacher_base import GurobiTeacherBase, DRealTeacherBase, SymPyTeacherBase
 
 WHEEL_BASE = 1.75  # m
 
@@ -29,6 +30,54 @@ NEW_ATAN_K_CTE_V_LIM = np.arctan(NEW_K_CTE_V_LIM)
 NEW_RAW_ANG_ERR_LIM = ANG_LIM + FORWARD_VEL * CYCLE_TIME
 
 
+class GEMStanleyDRealTeacher(DRealTeacherBase):
+
+    def __init__(self, name="gem_stanley", norm_ord=2, delta=0.001) -> None:
+        super().__init__(name=name,
+                         state_dim=3, perc_dim=2, ctrl_dim=1, norm_ord=norm_ord, delta=delta)
+
+    def _add_system(self) -> None:
+        self._set_var_bound(self._old_state, lb=(-np.inf, -CTE_LIM, -ANG_LIM), ub=(np.inf, CTE_LIM, ANG_LIM))
+        self._set_var_bound(self._new_state, lb=(-np.inf, -CTE_LIM, -ANG_LIM), ub=(np.inf, CTE_LIM, ANG_LIM))
+        self._set_var_bound(self._percept, lb=(-CTE_LIM, -ANG_LIM), ub=(CTE_LIM, ANG_LIM))
+        self._set_var_bound(self._control, lb=(-STEERING_LIM,), ub=(STEERING_LIM,))
+
+        # Variable Aliases
+        old_x, old_y, old_yaw = self._old_state
+        new_x, new_y, new_yaw = self._new_state
+        cte, phi = self._percept
+        steering, = self._control
+
+        self._not_inv_cons.extend([
+            # Control
+            steering == dreal.Min(dreal.Max(phi + dreal.atan(cte*(K_P / FORWARD_VEL)), -STEERING_LIM), STEERING_LIM),
+            # Dynamics
+            new_x == old_x + FORWARD_VEL*CYCLE_TIME*dreal.cos(old_yaw + steering),
+            new_y == old_y + FORWARD_VEL*CYCLE_TIME*dreal.sin(old_yaw + steering),
+            new_yaw == old_yaw + dreal.sin(steering)*FORWARD_VEL*CYCLE_TIME/WHEEL_BASE,
+        ])
+
+    def _add_unsafe(self) -> None:
+        # Variable Aliases
+        old_x, old_y, old_yaw = self._old_state
+        new_x, new_y, new_yaw = self._new_state
+
+        if self._norm_ord == 1:
+            old_err = abs(old_y) + abs(old_yaw)
+            new_err = abs(new_y) + abs(new_yaw)
+        elif self._norm_ord == 2:
+            old_err = dreal.sqrt(old_y**2 + old_yaw**2)
+            new_err = dreal.sqrt(new_y**2 + new_yaw**2)
+        else:
+            assert self._norm_ord == "inf"
+            old_err = dreal.Max(abs(old_y), abs(old_yaw))
+            new_err = dreal.Max(abs(new_y), abs(new_yaw))
+
+        self._not_inv_cons.extend([
+            old_err <= new_err
+        ])
+
+
 class GEMStanleyGurobiTeacher(GurobiTeacherBase):
     def __init__(self, name="gem_stanley") -> None:
         super().__init__(name=name,
@@ -122,16 +171,32 @@ class GEMStanleyGurobiTeacher(GurobiTeacherBase):
         m.addConstr(new_V >= old_V)  # Tracking error is increasing (UNSAFE)
 
 
-class GEMStanleyDRealTeacher(DRealTeacherBase):
+class GEMStanleySymPyTeacher(SymPyTeacherBase):
+    CTE_LIM = sympy.Rational("2.0")
+    ANG_LIM = sympy.pi / 2
+    STEERING_LIM = sympy.Rational("0.61")
+    K_P = sympy.Rational("0.45")
+    FORWARD_VEL = sympy.Rational("2.8")
+    CYCLE_TIME = sympy.Rational("0.05")
+    WHEEL_BASE = sympy.Rational("1.75")
 
-    def __init__(self, name="gem_stanley", norm_ord=2, delta=0.001) -> None:
+    def __init__(self, name="gem_stanley", norm_ord=2) -> None:
         super().__init__(name=name,
-                         state_dim=3, perc_dim=2, ctrl_dim=1, norm_ord=norm_ord, delta=delta)
+                         state_dim=3, perc_dim=2, ctrl_dim=1, norm_ord=norm_ord)
 
     def _add_system(self) -> None:
-        self._set_var_bound(self._old_state, lb=(-np.inf, -CTE_LIM, -ANG_LIM), ub=(np.inf, CTE_LIM, ANG_LIM))
-        self._set_var_bound(self._percept, lb=(-CTE_LIM, -ANG_LIM), ub=(CTE_LIM, ANG_LIM))
-        self._set_var_bound(self._control, lb=(-STEERING_LIM,), ub=(STEERING_LIM,))
+        self._set_var_bound(self._old_state,
+                            lb=(-sympy.oo, -self.CTE_LIM, -self.ANG_LIM),
+                            ub=(sympy.oo, self.CTE_LIM, self.ANG_LIM))
+        self._set_var_bound(self._new_state,
+                            lb=(-sympy.oo, -self.CTE_LIM, -self.ANG_LIM),
+                            ub=(sympy.oo, self.CTE_LIM, self.ANG_LIM))
+        self._set_var_bound(self._percept,
+                            lb=(-self.CTE_LIM, -self.ANG_LIM),
+                            ub=(self.CTE_LIM, self.ANG_LIM))
+        self._set_var_bound(self._control,
+                            lb=(-self.STEERING_LIM,),
+                            ub=(self.STEERING_LIM,))
 
         # Variable Aliases
         old_x, old_y, old_yaw = self._old_state
@@ -139,13 +204,20 @@ class GEMStanleyDRealTeacher(DRealTeacherBase):
         cte, phi = self._percept
         steering, = self._control
 
+        err = phi + sympy.atan2(cte*self.K_P, self.FORWARD_VEL)
+        clipped_err = sympy.Piecewise(
+            (self.STEERING_LIM, err > self.STEERING_LIM),
+            (-self.STEERING_LIM, err < -self.STEERING_LIM),
+            (err, True)
+        )
+
         self._not_inv_cons.extend([
             # Control
-            steering == dreal.Min(dreal.Max(phi + dreal.atan(cte*(K_P / FORWARD_VEL)), -STEERING_LIM), STEERING_LIM),
+            sympy.Eq(steering, clipped_err),
             # Dynamics
-            new_x == old_x + FORWARD_VEL*CYCLE_TIME*dreal.cos(old_yaw + steering),
-            new_y == old_y + FORWARD_VEL*CYCLE_TIME*dreal.sin(old_yaw + steering),
-            new_yaw == old_yaw + dreal.sin(steering)*FORWARD_VEL*CYCLE_TIME/WHEEL_BASE,
+            sympy.Eq(new_x, old_x + self.FORWARD_VEL*self.CYCLE_TIME*sympy.cos(old_yaw + steering)),
+            sympy.Eq(new_y, old_y + self.FORWARD_VEL*self.CYCLE_TIME*sympy.sin(old_yaw + steering)),
+            sympy.Eq(new_yaw, old_yaw + sympy.sin(steering)*self.FORWARD_VEL*self.CYCLE_TIME/self.WHEEL_BASE),
         ])
 
     def _add_unsafe(self) -> None:
@@ -154,22 +226,22 @@ class GEMStanleyDRealTeacher(DRealTeacherBase):
         new_x, new_y, new_yaw = self._new_state
 
         if self._norm_ord == 1:
-            old_err = abs(old_y) + abs(old_yaw)
-            new_err = abs(new_y) + abs(new_yaw)
+            old_err = sympy.Abs(old_y) + sympy.Abs(old_yaw)
+            new_err = sympy.Abs(new_y) + sympy.Abs(new_yaw)
         elif self._norm_ord == 2:
-            old_err = dreal.sqrt(old_y**2 + old_yaw**2)
-            new_err = dreal.sqrt(new_y**2 + new_yaw**2)
+            old_err = sympy.sqrt(old_y**2 + old_yaw**2)
+            new_err = sympy.sqrt(new_y**2 + new_yaw**2)
         else:
             assert self._norm_ord == "inf"
-            old_err = dreal.Max(abs(old_y), abs(old_yaw))
-            new_err = dreal.Max(abs(new_y), abs(new_yaw))
+            old_err = sympy.Max(sympy.Abs(old_y), sympy.Abs(old_yaw))
+            new_err = sympy.Max(sympy.Abs(new_y), sympy.Abs(new_yaw))
 
         self._not_inv_cons.extend([
             old_err <= new_err
         ])
 
 
-def test_gem_stanley_teacher():
+def test_gem_stanley_dreal_teacher():
     a_mat = np.asfarray([[0, -1, 0],
                          [0, 0, -1]])
     b_vec = np.zeros(2)
@@ -189,8 +261,27 @@ def test_gem_stanley_teacher():
     # teacher.dump_model()
     res = teacher.check((a_mat, b_vec, coeff_mat, cut_vec))
     print(res)
+
+
+def test_gem_stanley_sympy_teacher():
+    teacher = GEMStanleySymPyTeacher(norm_ord=2)
+    teacher.set_old_state_bound(
+        lb=(-sympy.oo, sympy.Rational("0.5"), 0.0),
+        ub=(sympy.oo, sympy.Rational("1.2"), sympy.pi/12)
+    )
+    teacher.dump_system_formula()
+
+
+def test_gem_stanley_gurobi_teacher():
+    teacher = GEMStanleyGurobiTeacher()
+    teacher.set_old_state_bound(
+        lb=(-np.inf, 0.5, 0.0625),
+        ub=(np.inf, 1.0, 0.25)
+    )
+    teacher.dump_model()
+    print(teacher.check(None))
     print(teacher.model())
 
 
 if __name__ == "__main__":
-    test_gem_stanley_teacher()
+    test_gem_stanley_sympy_teacher()
-- 
GitLab