#!/usr/bin/env python # coding: utf-8 # ## CS 458: Goals # ### Robot without a Cause #

# # In recent years, we have witnessed impressive advances in computer technology based on statistical models of machine learning. Techniques such as deep learning (based on neural networks), hidden Markov models, naive Bayesian models, decision trees (and forests, which are collections of trees), linear and logistic regression, and others, have been labelled collectively as big data or data science or machine learning or, spuriously, artificial intelligence. By and large, these are statistical techniques that have been around for decades. What has changed is that we now have access to enormous data sets and the concomitant computer capacity to process these data sets. # # In the past, a key issue in statistics was how to select a representative sample from a population. People are familiar with opinion polling data that have a plus or minus range of uncertainty. If the opinion polling guys were able to speak with every single person, there would be no range of error. Essentially, that is what now happens with big data. Instead of taking a sample of words from Shakespeare or DNA base pairs from an e coli genome or Facebook likes or self-driving car left turn examples for statistical analysis or words and phrases that appear on web pages, the researchers can examine the entire population. There is no error range. # # Admittedly, the data - except for Shakespeare and maybe the e coli genome - may change over time, so there is a need to update the results. # # Ash Carter: “transparent accountability” # # # # Planner shrdlu # # # Driverless car: take teenager to liquor store, to ER when in diabetic shock, to police station when hijacked # The term artificial intelligence means different things to different people. It also changes over time. Alan Turing, the British mathematician, breaker of the WWII German Enigma code, and father of both computer science and artificial intelligence, acknowledged that the very idea of machine intelligence was audacious. He knew that people would be hesitant to ascribe intelligence to a computer. He proposed an experiment in which a human subject would communicate via teletype to either another human or a computer program. If the human subject could not tell the difference, then we would conclude that the computer program was intelligent. # # Over the years, there have been many milestones for computer intelligence. The first computer language compilers, which converted statements in high level languages like FORTRAN or ALGOL into machine code were termed automatic programming. They were performing tasks that previously required highly trained and skilled humans to perform. # # Following Turing, artificial intelligence got its start at an academic workshop at Dartmouth in the summer of 1956. The workshop was organized by John McCarthy (the inventor of the LISP programming language). Other prominent participants included Marvin Minsky, Allen Newell, Claude Shannon, Herbert Simon, Oliver Selfridge, John Nash, Arthur Samuel, and John Holland. Most of the major breakthroughs in the field for the following decades can be traced to this meeting. These advances included new programming languages, theorem proving, champion chess and checkers programs, machine learning, software engineering techniques, robotics, vision, and speech recognition. # # As the computer programs would achieve each new milestone, many observers would claim that we had arrived at artificial intelligence, which, alas, was just not the case. Indeed, part of the perception problem was due to the hubris of the Dartmouth participants and their colleagues. They would promise that in 10 years or so, they would solve the “vision problem” or the “natural language problem” or the “planning problem.” Their enthusiasm and confidence was similar to that of medical researchers who treat each new diagnostic or treatment advance as a major breakthrough that will rid mankind of disease. Antibiotics, anaesthesia, and x-rays are certainly salutary developments, but they do not represent a universal panacea. # # Most people recognize that modern medicine, even with its impressive advances, has not conquered all disease. People will still die. For better or worse, artificial intelligence, for much of the world, continues to have the misleading reputation of, well, intelligence. # # Computer programs do not display human intelligence anymore than penicillin or morphine cure all disease. The predictions and fears surrounding artificial intelligence, often inspired by science fiction, seem to imply that robots can be functioning replacements for humans in any way imaginable. # # Philosophers have contemplated human intelligence for thousands of years. Once artificial intelligence reared its head, philosophers paid attention. In particular, John Searle wrote a famous paper “Minds, Brains, and Programs” in 1980. # One of Searle's key observations is that the computer cannot be intelligent because it lacks intentionality. The program that plays chess does not want to play chess. The self driving car does not want to drive. It is not driving because it wants to go to school or go to the movies. # # These so-called intelligent programs lack intentionality. They have no motivation. We have a robot without a cause. # # To some extent, the response to this observation is "so what." An airplane, unlike a bird, does not want to fly. Nevertheless, airplanes are extraordinarily useful and marvelous machines. The modern world is a much better place because of air travel. # # However, Searle is not arguing that computer programs are not useful. He is saying that the are not intelligent due to their lack of intentionality. # I believe that is a fair criticism. Computers do not have intentionality. # # This raises the question: is it possoble for computers to have intentionality? Can we create a computer that has goals and desires, and can act based on those beliefs? # # We will explore that question throughout this document. # ### A Goal Class in Python # For a computer to have goals, we need a goal data structure. Let's give it a try. We start with one flavor of goals: an issue. # # We use standard Python object oriented programming techniques. Nothing fancy here. # # Unlike most academic papers, we will not limit our argument to theories, explanations, # formulas, and examples. Thanks to the marvels of Jupyter Notebooks, we will include actual, honest to God, executable code (in Python). Like so many advances in the field of computer science, the idea of combining text and code can be traced to Donald Knuth. # He pioneered literate programming back in 1984. Knuth named his # implementation WEB, since this was one of the few three-letter English words # that had not been applied to computing. # In[2]: class issue: ''' Class for issues ''' # keep track of each instance of an issue. count = 0 # how many issues have we created? issues = {} # store the issues in a dictionary, aka, a hash table. def __init__(self, name): ''' This is the constructor for an issue. It is invoked with the class name and the name of the issue, e.g., issue("abortion") We use the Python string method upper() to convert the name to upper case. If the issue is already in the dictionary, we ignore this instance. Otherwise, we add it to the dictionary. We assign a sequential count to the instance and increment the class count. We stick the new issue in dictionary.''' self.name = name.upper() if self.name not in issue.issues: self.count = issue.count issue.count += 1 issue.issues[self.name] = self def __repr__(self): ''' Print out a representation that evaluates to this issue.''' return f'issue({self.name!r})' def __str__(self): ''' Return string version of the issue that includes its name and count. ''' return f"" def __eq__(self, other): ''' Overload == operator. Two issues must match issue name. ''' return self.name == other.name # OK. This is pretty minimal. We will expand it later on. # # As we slowly expand the definitions, the reader will no doubt raise objections # that the model does not do this or the model cannot handle this case. # Those objections will mostly be true. However, we urge patience. The model # has to do something before it can do everything. # # The author observes that such objections may fall prey to the fallacy of the # counter-example. That is, the argument that a model is invalid because it # fails to handle this case or that. For example, aspirin is of no use because it does not cure cancer. # # Therefore, we advise patience, which Ambrose Bierce defined as A minor form # of despair disguised as a virtue. # # # # We can now create a couple of issues. # In[3]: i1 = issue('abortion rights') i2 = issue('gun control') i3 = issue('covid') i4 = issue('equality') i5 = issue('freedom of speech') # In[4]: i1 # In[5]: print(i1) # The value of the issue is rendered with the repr method. The print function calls the str method. # # We can demonstrate the overloaded == and (implicitly defined) != operators. # In[6]: i1 == i1 # In[7]: i1 == i2 # In[8]: i1 != i2 # The issue.issues dictionary is useful. # In[9]: issue.issues # In[10]: issue.issues['ABORTION RIGHTS'] # In[11]: # issue.issues['DEMOCRACY'] # You can catch this KeyError exception, or use the in operator. # In[12]: 'ABORTION RIGHTS' in issue.issues # In[13]: 'DEMOCRACY' in issue.issues # In[14]: 'abortion' in issue.issues # The issue name must be upper case to match. # These issues are diverse. There are some issues that usually have a valence. For example, most people are either pro-abortion (or pro-choice) or anti-abortion (or pro-life). Similarly, there are gun control advocates and supporters of the second amendment. # # We assume almost everyone is opposed to the corona virus. However, there are people who are pro-vaccine and there are anti-vaxxers. There are people in favor of mask mandates and those opposed. We can add issues for those groups. # In[15]: i6 = issue('covid vaccine') i7 = issue('mask mandates') # We want to be able to represent the positions of supporting or opposing abortion or vaccines or gun control or mask mandates. We also want to capture the fact that even people who agree on a position may not be equally adamant. One person who opposes abortion may bomb a Planned Parenthood clinic. Another may merely vote for a conservative politician. We want to be able to represent the spectrum of intensity. # # Our initial attempt is to create a stance data structure that comprises # - the issue # - the side (pro or con) # - the importance (A, B, or C) # where is A is very important, B is moderate, and C is minimal. # # Why not use numbers to represent importance? Good question. Numbers can indeed capture the variation of intensity or commitment. However, once you start using numbers, there is the temptation to use them in inappropaite ways, such as addition, multiplication, square roots, exponents, logarithms, and on and on. It is a slippery slope. # # Economic decision theory originally did not require numbers - just ordinal ranking. We can achieve ordinal ranking from A, B, and C. It will serve to rein in any tendency to numerical exuberance. # # Here is the stance class. # In[36]: class stance: ''' Class for importance and side on a given issue.''' count = 0 stances = [] def __init__(self, issuename, side='pro', importance='A'): ''' Constructor for stance(). If the issuename is not already an issue, create a new issue ''' if not issuename.upper() in issue.issues: issue(issuename) self.issue = issue.issues[issuename.upper()] self.side = side.upper() self.importance = importance.upper() self.count = stance.count stance.count += 1 stance.stances.append(self) def __repr__(self): ''' Print out code that evaluates to this stance.''' return f'stance({self.issue.name!r}, {self.side!r}, {self.importance!r})' def __str__(self): ''' Return string version of self ''' return f"" def __eq__(self, other): ''' Overload == operator. Two stances must match issue and side, though not importance. ''' return self.issue == other.issue and self.side == other.side def copy(self): ''' Clone a stance. New stance has same issue, side, and importance. ''' return stance(self.issue.name, self.side, self.importance) def __hash__(self): ''' hash() function for stance. Need this for set() to remove duplicates. Note: do not need to include importance. Match is on issue and side only. ''' return hash((self.issue.name, self.side)) def __lt__(self, other): ''' Comparison operator < to allow sorting stances. ''' return self.issue.name + self.side < other.issue.name + other.side # In[37]: s1 = stance('abortion rights') s2 = stance('abortion rights', 'con', 'b') s3 = stance('free speech') # In[38]: s1 # In[39]: print(s1) # In[40]: s1 == s2 # In[41]: s1 != s2 # In[42]: s1 == stance('abortion rights', 'pro', 'c') # In[43]: s3 == s3.copy() # In[44]: sorted([s1,s2,s3]) # ## Agent Class # # An agent has goals - or stances - that reflects her desires and guides her choices. # # Below is a first draft of an agent class. We will later expand this definition to include relationships with other agents. # In[68]: class agent: '''Class for agents who have goals.''' count = 0 agents = [] def __init__(self, name, pronouns='he him his'): ''' Constructor for agent with name.''' self.name = name self.pronouns = pronouns self.goals = [] self.count = agent.count agent.count += 1 agent.agents.append(self) def __repr__(self): ''' Print out agent so that it can evaluate to itself.''' return f"agent({self.name!r})" def __str__(self): '''Return agent as a string.''' return f"" def add_goal(self, goal): '''Add goals (stances) without duplicates.''' if not goal in self.goals: self.goals.append(goal) def pp(self): '''Pretty print agent information.''' result = f"Name:\t{self.name}" if self.goals: result += f"\nGoals:\t{self.goals}" return result def __eq__(self, other): ''' Overload == operator. Are two agents equal by name and goals? ''' return self.name == other.name and sorted(self.goals) == sorted(other.goals) def copy(self): ''' Clone the agent, including name, and goals. ''' newagent = agent(self.name) newagent.goals = self.goals[:] return newagent # Question for the reader: why write the copy() constructor with #

# newagent.goals = self.goals[:]
# 
# instead of simply #
# newagent.goals = self.goals
# 
# In[69]: [1,2,3] == [3,2,1] # In[70]: sorted([1,2,3]) == sorted([3,2,1]) # In[71]: a1 = agent('James Bond') a2 = agent('Mata Hari') # In[72]: a1 # In[73]: print(a2) # In[74]: a1.add_goal(s1) a1.add_goal(s2) a1.add_goal(s3) a1.add_goal(s1) # In[75]: a1.goals # In[76]: a1.pp() # In[77]: print(a1.pp()) # In[81]: a1 == a2 # In[82]: a1 == a1.copy() # We can now create an agent who has a collection of goals, or more precisely, stances. # # These stances comprise preferences. An agent makes choices that reflect these preferences. # # The first step is to create agents that can make very minimal decisions. We propose an initial stage in which an agent is given a state of the world and evaluates it. The decision function is simply the predicate like. Does the agent like the outcome or not? # # The next step is to give the agent a choice between two outcomes: A or B. There are more possibilities. The agent may prefer A to B, B to A, be indifferent between the choices, or not wish either. # #

Likes

# # # # # In[83]: def likes(agt, obj): proresult = [] conresult = [] for g in agt.goals: for s in obj: if g == s: proresult.append(s) if g != s: conresult.append(s) if proresult and conresult: return ("both", proresult, conresult) if proresult: return (True, proresult) if conresult: return (False, conresult) else: return False # In[84]: print(a1.pp()) # In[85]: likes(a1, [stance('abortion','con')]) # In[ ]: #

English Output

# In[ ]: import random def english(agent, stance): side = stance.side match side: case "PRO": return english_pro(agent, stance) case "CON": return english_con(agent, stance) case _: return "default" def english_pro(agent, stance): imp = stance.importance iss = stance.issue.name.lower() phrases = { "A": [f"{verb(agent,'be','present')} unwavering in {pronoun(agent,'poss')} support of {iss}", f"{verb(agent,'stress','present')} {pronoun(agent,'poss')} long-standing support of {iss}", f"{verb(agent,'emphasize','present')} {pronoun(agent,'poss')} deep-rooted support of {iss}", f"unwaveringly {verb(agent,'endorse','present')} {iss}" ], "B": [f"strongly {verb(agent,'support','present')} {iss}", f"readily {verb(agent,'endorse','present')} {iss}" ], "C": [f"{verb(agent,'approve','present')} of {iss}", f"{verb(agent,'endorse','present')} {iss}" ] } print(random.choice(phrases[imp])) irreg = { "be": "is" } def verb(agent, v, tense): if v in irreg.keys(): return irreg[v] if v[-1] == 's': return v+'es' return v+'s' def pronoun(agent, case): pronouns = agent.pronouns.split() cases = {'subj': 0, 'obj': 1, 'poss': 2} return pronouns[cases[case]] def english_con(agent, stance): imp = stance.importance iss = stance.issue.name.lower() phrases = { "A": [f"{verb(agent,'be','present')} unwavering in {pronoun(agent,'poss')} opposition to {iss}", f"{verb(agent,'stress','present')} {pronoun(agent,'poss')} long-standing opposition to {iss}", f"{verb(agent,'emphasize','present')} {pronoun(agent,'poss')} deep-rooted opposition to {iss}", f"unwaveringly {verb(agent,'endorse','present')} {iss}" ], "B": [f"strongly {verb(agent,'oppose','present')} {iss}", f"{verb(agent,'be', 'present')} strongly opposed to {iss}", f"firmly {verb(agent,'oppose','present')} {iss}", f"readily {verb(agent,'endorse','present')} {iss}" ], "C": [f"{verb(agent,'be','present')} opposed to {iss}", f"{verb(agent,'be','present')} against {iss}", f"{verb(agent,'object','present')} to {iss}", f"{verb(agent,'oppose','present')} {iss}" ] } print(random.choice(phrases[imp])) s4 = stance('abortion rights', 'pro', 'b') s5 = stance('abortion rights', 'pro', 'c') s6 = stance('abortion rights', 'con', 'a') s7 = stance('abortion rights', 'con', 'b') s8 = stance('abortion rights', 'con', 'c')