AI Zone Admin Forum Add your forum

NEWS: Chatbots.org survey on 3000 US and UK consumers shows it is time for chatbot integration in customer service!read more..

AIML Sample: Addition of whole numbers in the millions (for Andres and others)
 
 

I thought I would jump in and offer some samples that will support Steve’s position that more is possible with AIML than appears at first glance.  Whether using AIML to perform math is the best idea or not I will leave to the judgement of others.  Like many chatbot developers, a few years back when I first learned of AIML I wanted to be able to perform math operations.  I developed some code that handled addition, subtraction, and multiplication of positive whole numbers but I grew weary of it when I got to division.  I have dusted off the old experiments and have cleaned them up a little and retested the addition functionality.

I just want to reaffirm that math is possible in AIML and will be covering how it can be done in this post.

Sample Output:

Human: what is 999999999999999 + 111111111111111
sienna2: answer: 1111111111111110

Human: what is 1234 + 39
sienna2: answer: 1273

The interesting thing about AIML, let’s say you wanted to build a math tutor chatbot and you wanted your chatbot to explain how it did the math, then you could:

Human: how did you calculate it
sienna2: Working backwards from the ones place, first I added 4 to 9 resulting in 13 . I carried the 1 . Then I added 3 to 3 along with the 1 that I carried last time getting 7 . Then I added 2 to 0 resulting in 2 . Then I added 1 to 0 resulting in 1 . The final result was 1273 .

I really would like to see a neural net or a graph query explain itself and how it arrived at the answer.  My explanation category is very basic and I have not spent any time adding logic to vary the language used.  If I spent more time on it I would track the ones, tens, hundreds, etc places and use that in my English descriptions of the calculations; that would be better for a math tutor chatbot.

 

 

 

 

 
  [ # 1 ]

Here is the heart of it.  I use “stacks” and I push each digit of each number onto a 2 different stacks and then pop them off in reverse order.  The following code performs math as a human would, one column at a time and very similar to the method Steve demonstrated in the previous post.  There are several categories referenced that I will follow up on and explain a step at a time.

Looping through each position I calculate the result of adding two single digit numbers together.  This is done with 100 categories of the form XMATH <digit> PLUS <digit>.  Just like a human the chatbot must know its addition and multiplication tables.

<?xml version="1.0" encoding="UTF-8"?>
<aiml versi>

<!-- 
This file requires the following other aiml files to work properly -->
<!-- 
File Dependencies: -->
<!-- 
Stacks.aiml -->
<!-- 
StringFunctions.aiml -->
<!-- 
Brain.aiml -->

<!-- 
Addition: -->
<
category><pattern>what is plus *</pattern>
<
template>answer: <srai>XMATHCALC <star/> PLUS <star index="2"/></srai>
</
template>
</
category>

<
category><pattern>how did you calculate it</pattern>
<
template><denormalize><srai>XCALCHOW</srai></denormalize></template>
</
category>

<
category>
<
pattern>XMATHCALC PLUS *</pattern>
<
template><think>
<
set name="ANSWER"></set>
<
set name="ANSWER2"></set>
<
set name="CARRYAMOUNT">0</set>
<
set name="ENDOFSTACKS">FALSE</set>
<
set name="THOUGHTS"></set>
<
set name="FIRSTPASS">TRUE</set>
<
srai>XPUSHALLEXPLODED STACK1 <star/></srai>
<
srai>XPUSHALLEXPLODED STACK2 <star index="2"/></srai>
<
set name="X"><srai>XPOP STACK1</srai></set>
<
set name="Y"><srai>XPOP STACK2</srai></set>
<
srai>XMATHCALC2 X PLUS Y</srai>
<
srai>XTHINK The final result was <get name="ANSWER"/> XPERIOD  </srai>
<
srai>XREMEMBER THOUGHTS AS XCALCHOW</srai>
</
think><get name="ANSWER"/></template>
</
category>

<
category>
<
pattern>XMATHCALC2 X PLUS Y</pattern>
<
template><think>
<
condition name="X">
  <
li value="">
    <
condition name="Y">
        <
li value=""><set name="ENDOFSTACKS">TRUE</set></li>
        <
li><set name="X">0</set></li>
    </
condition>
  </
li>
  <
li><condition name="Y" value=""><set name="Y">0</set></condition></li>
</
condition>
<
condition name="ENDOFSTACKS">
    <
li value="FALSE">
        <
set name="ANSWER"><srai>XMATHCALC3 CARRYAMOUNT <srai>XMATH <get name="X"/> PLUS <get name="Y"/></srai></srai> <get name="ANSWER"/></set>
        <
set name="X"><srai>XPOP STACK1</srai></set>
        <
set name="Y"><srai>XPOP STACK2</srai></set>
    <
srai>XMATHCALC2 X PLUS Y</srai></li>
    <
li><set name="ANSWER"><srai>XCOMPACT <srai>XMATHCALC3 CARRYAMOUNT 0 0</srai> <get name="ANSWER"/></srai></set></li>
</
condition>
</
think></template>
</
category
 

 
  [ # 2 ]

The addition requires a calculation for determining the amount carried over from the last operation and the amount carried over from the current operation.  The category: XMATHCALC3 CARRYAMOUNT <carry amount> <result>
returns a single digit result and remembers the “OLDCARRYAMOUNT” for use in the next math calculation.

My categories for XMATH <digit> PLUS <digit> return 2 digits.  These two digits are passed to XMATHCALC3 category below:

<category>
<
pattern>XMATHCALC3 CARRYAMOUNT * *</pattern>
<
template><think>
<
set name="CARRYAMOUNT"><star index="1"/></set>
<
set name="NEWCARRYAMOUNT">0</set>
<
set name="RESULT"><star index="2"/></set>
<
condition name="OLDCARRYAMOUNT">
    <
li value="0"></li>
    <
li value="1">
        <
condition name="RESULT">
            <
li value="0"><set name="RESULT">1</set></li>
            <
li value="1"><set name="RESULT">2</set></li>
            <
li value="2"><set name="RESULT">3</set></li>
            <
li value="3"><set name="RESULT">4</set></li>
            <
li value="4"><set name="RESULT">5</set></li>
            <
li value="5"><set name="RESULT">6</set></li>
            <
li value="6"><set name="RESULT">7</set></li>
            <
li value="7"><set name="RESULT">8</set></li>
            <
li value="8"><set name="RESULT">9</set></li>
            <
li value="9">0<set name="NEWCARRYAMOUNT">1</set><set name="RESULT">0</set>
            </
li>
        </
condition>
    </
li>
</
condition>
<
srai>XDOCUMENTMATH PLUS</srai>
<
condition name="NEWCARRYAMOUNT">
    <
li value="0"><set name="OLDCARRYAMOUNT"><get name="CARRYAMOUNT"/></set></li>
    <
li><set name="OLDCARRYAMOUNT"><get name="NEWCARRYAMOUNT"/></set></li>
</
condition>
</
think><get name="RESULT"/></template>
</
category
 

 
  [ # 3 ]

Here is a sample of the first 30 categories for XMATH <digit> plus <digit>

You will need 100 for every combination of the digits 0-9.
I think you could get away with only half the categories where order of the numbers is not considered and if a match was not found then you could switch the numbers and try again but then the engine would have to perform two passes of the categories.  I am not sure what would be more efficient.

<category><pattern>XMATH 0 PLUS 0</pattern><template>0 0</template></category>
<
category><pattern>XMATH 0 PLUS 1</pattern><template>0 1</template></category>
<
category><pattern>XMATH 0 PLUS 2</pattern><template>0 2</template></category>
<
category><pattern>XMATH 0 PLUS 3</pattern><template>0 3</template></category>
<
category><pattern>XMATH 0 PLUS 4</pattern><template>0 4</template></category>
<
category><pattern>XMATH 0 PLUS 5</pattern><template>0 5</template></category>
<
category><pattern>XMATH 0 PLUS 6</pattern><template>0 6</template></category>
<
category><pattern>XMATH 0 PLUS 7</pattern><template>0 7</template></category>
<
category><pattern>XMATH 0 PLUS 8</pattern><template>0 8</template></category>
<
category><pattern>XMATH 0 PLUS 9</pattern><template>0 9</template></category>

<
category><pattern>XMATH 1 PLUS 0</pattern><template>0 1</template></category>
<
category><pattern>XMATH 1 PLUS 1</pattern><template>0 2</template></category>
<
category><pattern>XMATH 1 PLUS 2</pattern><template>0 3</template></category>
<
category><pattern>XMATH 1 PLUS 3</pattern><template>0 4</template></category>
<
category><pattern>XMATH 1 PLUS 4</pattern><template>0 5</template></category>
<
category><pattern>XMATH 1 PLUS 5</pattern><template>0 6</template></category>
<
category><pattern>XMATH 1 PLUS 6</pattern><template>0 7</template></category>
<
category><pattern>XMATH 1 PLUS 7</pattern><template>0 8</template></category>
<
category><pattern>XMATH 1 PLUS 8</pattern><template>0 9</template></category>
<
category><pattern>XMATH 1 PLUS 9</pattern><template>1 0</template></category>

<
category><pattern>XMATH 2 PLUS 0</pattern><template>0 2</template></category>
<
category><pattern>XMATH 2 PLUS 1</pattern><template>0 3</template></category>
<
category><pattern>XMATH 2 PLUS 2</pattern><template>0 4</template></category>
<
category><pattern>XMATH 2 PLUS 3</pattern><template>0 5</template></category>
<
category><pattern>XMATH 2 PLUS 4</pattern><template>0 6</template></category>
<
category><pattern>XMATH 2 PLUS 5</pattern><template>0 7</template></category>
<
category><pattern>XMATH 2 PLUS 6</pattern><template>0 8</template></category>
<
category><pattern>XMATH 2 PLUS 7</pattern><template>0 9</template></category>
<
category><pattern>XMATH 2 PLUS 8</pattern><template>1 0</template></category>
<
category><pattern>XMATH 2 PLUS 9</pattern><template>1 1</template></category
 

 
  [ # 4 ]

There is a call to XDOCUMENTMATH PLUS in the XMATHCALC3 category. 
A category for XTHINK appends text to a predicate “THOUGHTS”.
A category for XREMEMBER TO THOUGHTS AS XCALCHOW uses the learn tag to remember whatever has been stored in the predicate “THOUGHTS” as a category XCALCHOW.

There is some maximum limit to the size of a predicate so the explanation text currently works for smaller numbers.
Comment out the call to
<srai>XDOCUMENTMATH PLUS</srai>
if you want to perform addition for numbers in the millions.

I use the <denormalize> tag to replace the XSPACE and XPERIOD with the appropriate punctuation when the results are returned to the user and I am working on better formatting.

The is room for much improvement in this NLG category but it illustrates what can be done.

Here is the code:

<category><pattern>XDOCUMENTMATH PLUS</pattern>
<
template><think>
    <
condition name="CARRYAMOUNT">
        <
li value="0"><set name="2DIGITAMOUNT"><get name="RESULT"/></set></li>
        <
li><set name="2DIGITAMOUNT"><get name="CARRYAMOUNT"/><get name="RESULT"/></set></li>
    </
condition>
    <
condition name="FIRSTPASS">
        <
li value="TRUE">
            <
set name="INTRO">Working backwards from the ones placefirst XSPACE </set>
            <
set name="FIRSTPASS">FALSE</set>
        </
li>
        <
li><set name="INTRO">Then XSPACE</set></li>
    </
condition>
    <
condition name="X">
        <
li value=""></li>
        <
li><srai>XTHINK <get name="INTRO"/> I added <get name="X"/> to <get name="Y"/> XSPACE </srai>
            <
condition name="OLDCARRYAMOUNT">
                <
li value="1">
                    <
srai>XTHINK along with the 1 that I carried last time XSPACE </srai>
                    <
condition name="RESULT">
                        <
li value="0"><srai>XTHINK ending up with 10 so I carried the 1 again XPERIOD</srai></li>
                        <
li><srai>XTHINK getting <get name="2DIGITAMOUNT"/> XPERIOD </srai></li>
                    </
condition>
                </
li>
                <
li>
                    <
srai>XTHINK resulting in <get name="2DIGITAMOUNT"/> XPERIOD </srai>
                </
li>
            </
condition>
            <
condition name="CARRYAMOUNT">
                <
li value="1"><srai>XTHINK XSPACE I carried the <get name="CARRYAMOUNT"/> XPERIOD </srai></li>
            </
condition>
        </
li>
    </
condition>
    </
think></template>
</
category

 

 

 
  [ # 5 ]

I figured out how to get around the predicate maximum length by using queues which I will describe in a separate post.  My call to XTHINK now stores up to 5 strings in the predicate “THOUGHTS” and then it writes the predicate to a queue which is 30 deep so I am storing up to 150 strings.  It is not smart enough to know the string length and it is up to me to keep every 5 strings under the maximum predicate limit (whatever it is).  When I call XREMEMBER THOUGHTS AS * it dumps the queue into the template of the learn tag with a pattern of whatever you name as *.  In this example I am using XCALCHOW for *.

So now when I ask the chatbot to add:

Human: what is 12345678901234567890 plus 11111111111111111111
sienna2: answer: 23456790012345679001

It can describe how it did it:

Human: how did you calculate it
sienna2: Working backwards from the ones place, first I added 0 to 1 resulting in 1 . Then I added 9 to 1 resulting in 10 . I carried the 1 . Then I added 8 to 1 along with the 1 that I carried last time ending up with 10 so I carried the 1 again . Then I added 7 to 1 along with the 1 that I carried last time getting 9 . Then I added 6 to 1 resulting in 7 . Then I added 5 to 1 resulting in 6 . Then I added 4 to 1 resulting in 5 . Then I added 3 to 1 resulting in 4 . Then I added 2 to 1 resulting in 3 . Then I added 1 to 1 resulting in 2 . Then I added 0 to 1 resulting in 1 . Then I added 9 to 1 resulting in 10 . I carried the 1 . Then I added 8 to 1 along with the 1 that I carried last time ending up with 10 so I carried the 1 again . Then I added 7 to 1 along with the 1 that I carried last time getting 9 . Then I added 6 to 1 resulting in 7 . Then I added 5 to 1 resulting in 6 . Then I added 4 to 1 resulting in 5 . Then I added 3 to 1 resulting in 4 . Then I added 2 to 1 resulting in 3 . Then I added 1 to 1 resulting in 2 . The final result was 23456790012345679001 . 

Rather than being limited to just describing math calculations, you can easily add descriptions of thoughts for any internal processing and then ask the chatbot what it was thinking or how did it arrive at that answer. 

It is useful for debugging as you can XTHINK step1, XTHINK step2, XTHINK step3 and then see how far the code executed.

So while AIML may have some limitations it does appear that performing 20-digit math addition problems is not one of them. 

 

 
  [ # 6 ]

Tres cool. That’s about all the comment I can manage, because I only just started in AIML and decoding all that (plus overcoming my math block) would be a project. But I love that you can calculate that high.

Question: If you wanted to set a condition based on a range of numbers is there a way to create a <li value=“1-5”> or do you have to do a diff <li value..> for each separate number?

 

 
  [ # 7 ]

Sorry but yes, you need a <li> for each one but once the function is written, you can use it as many times as you like.

 

 
  [ # 8 ]

That is what I thought. But I figured I better ask and be sure. No sense doing all that typing if there is an easier way. I wish there were more online tutorials for AIML. Figuring out a few of the basic rules has been difficult only because they are inconsistently scattered across various locations. Once I have this all figured out I may just gather it all up and organize it. I love to organize. A place for everything and everything in its place!

:D

 

 
  [ # 9 ]

Here is another question. I am playing with getting my AI to give me the time. It is spitting out GMT, but I want it to give me PST/PDT (GMT - 8…not sure if that changes depending on standard or daylight time actually). Is that going to have a math related solution?

 

 
  [ # 10 ]

No need for math here. You can work with timezones and locales:

<category>
<
pattern>WHAT TIME IS IT</pattern>
<
template>According to my watchit is about <date locale="en_GB" timez format="%I:%M %p" jformat="h:mm a"/></template>
</
category

timez should read “timezone=“0”. The webpage formats it strangely for some reason. The 2 different formats are for using a standard AIML interpreter and Program AB or a Java based one. The interpreter should be able to pick out the format it needs.

Human: What time is it?
Bot: According to my watch, it is about 09:53 PM.

I use the en_GB date_locale but you should choose one suitable for yourself. If you can’t find one, try using timezone=”-8”
You can find out more here:
http://www.alicebot.org/aiml/aaa/Date.txt

 

 
  [ # 11 ]

Thanks!
I am getting there. Slowly but surely!

 

 
  [ # 12 ]

Everyone starts with baby steps.

 

 
  login or register to react