You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@jena.apache.org by L B <da...@gmail.com> on 2023/04/03 18:52:33 UTC

Re: Add rule at runtime, without trigger not-related rule?

Hi Dave,

Thanks for the update.

The Jena version I am using is 4.2.0.
[image: image.png]

Checked Jena version 4.5.0. This API seems gone.

The system we are developing may have thousands of rules. To make the
engine more efficient,  we want some rules to be loaded/unloaded
dynamically.  For example, the driving related rules are loaded when the
user is driving. When the user gets home, those driving rules will be
unloaded.

In Jena 4.5 API, the reasoner has addRules API, but I do not know how to
make it work.

//inf is the InfModel created already.
GenericRuleReasoner reasoner = (GenericRuleReasoner) inf.getReasoner();
reasoner.setMode(GenericRuleReasoner.HYBRID);
reasoner.setDerivationLogging(true);
reasoner.setTraceOn(true);

List<Rule> rules = Rule.parseRules(ruleStr);
reasoner.addRules(rules);
inf.prepare();

The rule added is never triggered.  Is there any way we could add/remove
rules programmatically without cause to rebuild InfModal and recheck all
the existing rules?


Dave Reynolds <da...@gmail.com> 于2023年3月30日周四 14:14写道:

> There's no support for dynamically adding forward rules to a "running"
> reasoner that I recall.
>
> You can dynamically add backward rules to a hybrid reasoner, but that's
> not relevant here.
>
> Your code example suggests you are calling addRule on a
> BasicForwardRuleInfGraph but, unless I'm missing something, that method
> is not in the jena source. Is it something you have added?
>
> You can add rules to a reasoner but then would essentially have to build
> a new InfGraph or reset the engine state of the current infgraph. Either
> way that's why you are seeing the original deduction retrigger.
>
> If you need to fire some action when adding triples to a model, and
> dynamically change the processing, I'd suggest using the listener
> machinery rather than the rule system.
>
> Dave
>
> On 28/03/2023 18:43, L B wrote:
> > I am currently still trying to figure out the solution.
> >
> > 1. If I do not call coreInfModel.prepare(), nothing happens during the
> > triple update which is WAD, I believe.
> > 2. When one triple is updated, all the rules which have true condition
> will
> > be triggered, rather than the rules related. I am expecting the other
> rules
> > will not be triggered unless its related triplets are updated. Is it
> > possible?
> >
> >
> > L B <da...@gmail.com> 于2023年3月24日周五 16:10写道:
> >
> >>
> >> 1) Init code:
> >>
> >> GenericRuleReasoner reasoner = new GenericRuleReasoner(new
> >> ArrayList<Rule>());
> >>              reasoner.setMode(GenericRuleReasoner.FORWARD);
> >>              mCoreInfModel = ModelFactory.createInfModel(reasoner,
> >> mCoreModel);
> >>
> >> 2) Statement exits
> >>   (:A :B :C)
> >>
> >> 3) Rule exists and triggered before
> >>
> >>              [ alwaysTrue:
> >>                  (:A :B :C)
> >>                  -> triggerAction
> >>              ]
> >>
> >> 4) Now, I am trying to add rules at runtime.
> >>
> >>               [ rule2:
> >>                  (:X :Y :Z)
> >>                  -> triggerAction2
> >>              ]
> >>
> >>          InfModel infModel = getCoreInfModel();
> >>          BasicForwardRuleInfGraph infGraph = (BasicForwardRuleInfGraph)
> >> infModel.getGraph();          infGraph.addRule(rule)
> >>          coreInfModel.prepare()
> >>
> >> 5) add triple (:X :Y :Z)
> >>
> >>          val infGraph = infModel.graph as BasicForwardRuleInfGraph
> >>         infGraph.add(triple)
> >>
> >> *The problem comes, both "alwaysTrue" and "rule2" triggered.*
> >>
> >> *I am expecting only "rule2" is triggered. *
> >>
> >> Is there anyway that only "rule2" can be triggered since the updated
> >> triple is only X, Y Z?
> >>
> >> Many thanks
> >>
> >>
> >>
> >
>

Re: Add rule at runtime, without trigger not-related rule?

Posted by Dave Reynolds <da...@gmail.com>.
Hi,

Responses inline ...

On 04/04/2023 23:19, L B wrote:
> Here is the API location.
> image.png

Note that this list doesn't support attachments so we can't see your images.

> As you suggested, I started to investigate the backward rules.  Here is 
> some experimental code I did

...

> I.could successfully add a backward rule. Thanks for the suggestion.  
> When adding triple (a C1 t) as below
> 
> infgraph.performAdd(Triple.create(a, C1, t));
> 
> I could see:  a @C2 t.  printed.     <--- backward rule triggered.
> 
> Regarding backward rule, I have a few questions:
> 1. It seems the backward rule does not support builtin primitives, such 
> as print()?  I did not see C2Print print anything.

Correct. A backward rule, by definition, is only invoked when a query 
matches the consequent of the rule. If a rule doesn't infer any triples 
then it'll never (need to be) invoked.

You should be able to use a primitive in the body of the rule even if 
it's just a side-effecting primitive like print. So something like (not 
tested):

   (?a MYDeduction ?c) <- (?a MYPremise ?c), print('I was run') .

> 2. I created a forward rule (C2ForwardPrint) at the very beginning.  If 
> I add triple as below
> 
> infgraph.performAdd(Triple.create(a, C2, t));
> 
> 'check check forward'  will be printed
> 
> However, if I add above (a C1 t) triple, nothing happens. I assume (a C1 
> t)  generats (a C2 t) from backward rule C1, then recursively calls the  
> C2ForwardPrint rule to print something?

No. The hybrid reasoner is just feed-forward. The forward rules run over 
the data, make deductions and can generate additional backward rules.

When you ask a query of the model then the original data, the extra 
forward deductions plus the backward rules are used to answer it. The 
backward rules are only used on demand and they don't materialize any 
new deduction triples.

Summary: the forward rules don't "see" the results of any backward rules.

> Is there anything wrong?

Well seems like it's working as expected. Whether that's what you need 
might be a different question :)

Dave

> 
> Dave Reynolds <dave.e.reynolds@gmail.com 
> <ma...@gmail.com>> 于2023年4月4日周二 02:20写道:
> 
>     Hi,
> 
>     Very mysterious. If I check the github repository as tagged for 4.2.0 I
>     can't see an addRule method in BasicForwardRuleInfGraph. Clearly I'm
>     missing something.
> 
>     You can add rules to a reasoner but you would then have to rebuild the
>     InfGraph/InfModel.
> 
>     Like I say, you can add backward rules to a running hybrid engine, if
>     that's any use but I don't believe there's any support for dynamically
>     adding forward rules.
> 
>     Dave
> 
>     On 03/04/2023 19:52, L B wrote:
>      > Hi Dave,
>      >
>      > Thanks for the update.
>      >
>      > The Jena version I am using is 4.2.0.
>      > image.png
>      >
>      > Checked Jena version 4.5.0. This API seems gone.
>      >
>      > The system we are developing may have thousands of rules. To make
>     the
>      > engine more efficient,  we want some rules to be loaded/unloaded
>      > dynamically.  For example, the driving related rules are loaded
>     when the
>      > user is driving. When the user gets home, those driving rules
>     will be
>      > unloaded.
>      >
>      > In Jena 4.5 API, the reasoner has addRules API, but I do not know
>     how to
>      > make it work.
>      >
>      > //inf is the InfModel created already.
>      > GenericRuleReasoner reasoner = (GenericRuleReasoner)
>     inf.getReasoner();
>      > reasoner.setMode(GenericRuleReasoner.HYBRID);
>      > reasoner.setDerivationLogging(true);
>      > reasoner.setTraceOn(true);
>      >
>      > List<Rule> rules = Rule.parseRules(ruleStr);
>      > reasoner.addRules(rules);
>      > inf.prepare();
>      >
>      > The rule added is never triggered.  Is there any way we could
>     add/remove
>      > rules programmatically without cause to rebuild InfModal and
>     recheck all
>      > the existing rules?
>      >
>      >
>      > Dave Reynolds <dave.e.reynolds@gmail.com
>     <ma...@gmail.com>
>      > <mailto:dave.e.reynolds@gmail.com
>     <ma...@gmail.com>>> 于2023年3月30日周四 14:14写道:
>      >
>      >     There's no support for dynamically adding forward rules to a
>     "running"
>      >     reasoner that I recall.
>      >
>      >     You can dynamically add backward rules to a hybrid reasoner,
>     but that's
>      >     not relevant here.
>      >
>      >     Your code example suggests you are calling addRule on a
>      >     BasicForwardRuleInfGraph but, unless I'm missing something,
>     that method
>      >     is not in the jena source. Is it something you have added?
>      >
>      >     You can add rules to a reasoner but then would essentially
>     have to
>      >     build
>      >     a new InfGraph or reset the engine state of the current infgraph.
>      >     Either
>      >     way that's why you are seeing the original deduction retrigger.
>      >
>      >     If you need to fire some action when adding triples to a
>     model, and
>      >     dynamically change the processing, I'd suggest using the listener
>      >     machinery rather than the rule system.
>      >
>      >     Dave
>      >
>      >     On 28/03/2023 18:43, L B wrote:
>      >      > I am currently still trying to figure out the solution.
>      >      >
>      >      > 1. If I do not call coreInfModel.prepare(), nothing happens
>      >     during the
>      >      > triple update which is WAD, I believe.
>      >      > 2. When one triple is updated, all the rules which have true
>      >     condition will
>      >      > be triggered, rather than the rules related. I am
>     expecting the
>      >     other rules
>      >      > will not be triggered unless its related triplets are
>     updated. Is it
>      >      > possible?
>      >      >
>      >      >
>      >      > L B <daydaydev@gmail.com <ma...@gmail.com>
>     <mailto:daydaydev@gmail.com <ma...@gmail.com>>> 于2023年3
>      >     月24日周五 16:10写道:
>      >      >
>      >      >>
>      >      >> 1) Init code:
>      >      >>
>      >      >> GenericRuleReasoner reasoner = new GenericRuleReasoner(new
>      >      >> ArrayList<Rule>());
>      >      >>              reasoner.setMode(GenericRuleReasoner.FORWARD);
>      >      >>              mCoreInfModel =
>     ModelFactory.createInfModel(reasoner,
>      >      >> mCoreModel);
>      >      >>
>      >      >> 2) Statement exits
>      >      >>   (:A :B :C)
>      >      >>
>      >      >> 3) Rule exists and triggered before
>      >      >>
>      >      >>              [ alwaysTrue:
>      >      >>                  (:A :B :C)
>      >      >>                  -> triggerAction
>      >      >>              ]
>      >      >>
>      >      >> 4) Now, I am trying to add rules at runtime.
>      >      >>
>      >      >>               [ rule2:
>      >      >>                  (:X :Y :Z)
>      >      >>                  -> triggerAction2
>      >      >>              ]
>      >      >>
>      >      >>          InfModel infModel = getCoreInfModel();
>      >      >>          BasicForwardRuleInfGraph infGraph =
>      >     (BasicForwardRuleInfGraph)
>      >      >> infModel.getGraph();          infGraph.addRule(rule)
>      >      >>          coreInfModel.prepare()
>      >      >>
>      >      >> 5) add triple (:X :Y :Z)
>      >      >>
>      >      >>          val infGraph = infModel.graph as
>     BasicForwardRuleInfGraph
>      >      >>         infGraph.add(triple)
>      >      >>
>      >      >> *The problem comes, both "alwaysTrue" and "rule2" triggered.*
>      >      >>
>      >      >> *I am expecting only "rule2" is triggered. *
>      >      >>
>      >      >> Is there anyway that only "rule2" can be triggered since
>     the updated
>      >      >> triple is only X, Y Z?
>      >      >>
>      >      >> Many thanks
>      >      >>
>      >      >>
>      >      >>
>      >      >
>      >
> 

Re: Add rule at runtime, without trigger not-related rule?

Posted by L B <da...@gmail.com>.
Here is the API location.
[image: image.png]

As you suggested, I started to investigate the backward rules.  Here is
some experimental code I did

    // Useful constants
    Node p = NodeFactory.createURI("p");
    Node q = NodeFactory.createURI("q");
    Node r = NodeFactory.createURI("r");
    Node s = NodeFactory.createURI("s");
    Node t = NodeFactory.createURI("t");
    Node a = NodeFactory.createURI("a");
    Node b = NodeFactory.createURI("b");
    Node c = NodeFactory.createURI("c");
    Node d = NodeFactory.createURI("d");
    Node C1 = NodeFactory.createURI("C1");
    Node C2 = NodeFactory.createURI("C2");
    Node C3 = NodeFactory.createURI("C3");
    Node ty = RDF.Nodes.type;
    Node sC = RDFS.Nodes.subClassOf;

    List<Rule> ruleList = Rule.parseRules("""

            [r1: (?a p ?c) <- (?a p ?b), (?b p ?c)]

            # Print primitive
            [C2ForwardPrint: (?a C2 ?c) -> print("check check forward")]

            -> tableAll().
            """);

    public void testBackward() {
        Graph test = Factory.createGraphMem();
        test.add(Triple.create(a, p, b));
        test.add(Triple.create(b, p, c));

        GenericRuleReasoner reasoner = (GenericRuleReasoner)
GenericRuleReasonerFactory.theInstance().create(null);
        reasoner.setRules(ruleList);
        reasoner.setMode(GenericRuleReasoner.HYBRID);
        reasoner.setTransitiveClosureCaching(true);

        //Bind data
        FBRuleInfGraph infgraph = (FBRuleInfGraph)reasoner.bind(test);
        infgraph.prepare();


        // Add backward rule programatically.
        List<Rule> ruleList2 = Rule.parseRules("""
            [C2Print: print("check check") <- (?a C2 ?c)]
            [C1: (?a C2 ?c) <- (?a C1 ?c)]
            -> tableAll().
            """);
        infgraph.addBRules(ruleList2);
        // do not necessary to call prepare();
        //infgraph.prepare();
        // insert triple
        infgraph.performAdd(Triple.create(a, C1, t));

        // check if a C2 t is generated.
        Iterator it = infgraph.find(null, C2, null);
        while (it.hasNext()) {
            System.out.println("#####  " + it.next());
        }
    }

I.could successfully add a backward rule. Thanks for the suggestion.  When
adding triple (a C1 t) as below

infgraph.performAdd(Triple.create(a, C1, t));

I could see:  a @C2 t.  printed.     <--- backward rule triggered.

Regarding backward rule, I have a few questions:
1. It seems the backward rule does not support builtin primitives, such as
print()?  I did not see C2Print print anything.
2. I created a forward rule (C2ForwardPrint) at the very beginning.  If I
add triple as below

infgraph.performAdd(Triple.create(a, C2, t));

'check check forward'  will be printed

However, if I add above (a C1 t) triple, nothing happens. I assume (a C1
t)  generats (a C2 t) from backward rule C1, then recursively calls the
C2ForwardPrint rule to print something?

Is there anything wrong?


Dave Reynolds <da...@gmail.com> 于2023年4月4日周二 02:20写道:

> Hi,
>
> Very mysterious. If I check the github repository as tagged for 4.2.0 I
> can't see an addRule method in BasicForwardRuleInfGraph. Clearly I'm
> missing something.
>
> You can add rules to a reasoner but you would then have to rebuild the
> InfGraph/InfModel.
>
> Like I say, you can add backward rules to a running hybrid engine, if
> that's any use but I don't believe there's any support for dynamically
> adding forward rules.
>
> Dave
>
> On 03/04/2023 19:52, L B wrote:
> > Hi Dave,
> >
> > Thanks for the update.
> >
> > The Jena version I am using is 4.2.0.
> > image.png
> >
> > Checked Jena version 4.5.0. This API seems gone.
> >
> > The system we are developing may have thousands of rules. To make the
> > engine more efficient,  we want some rules to be loaded/unloaded
> > dynamically.  For example, the driving related rules are loaded when the
> > user is driving. When the user gets home, those driving rules will be
> > unloaded.
> >
> > In Jena 4.5 API, the reasoner has addRules API, but I do not know how to
> > make it work.
> >
> > //inf is the InfModel created already.
> > GenericRuleReasoner reasoner = (GenericRuleReasoner) inf.getReasoner();
> > reasoner.setMode(GenericRuleReasoner.HYBRID);
> > reasoner.setDerivationLogging(true);
> > reasoner.setTraceOn(true);
> >
> > List<Rule> rules = Rule.parseRules(ruleStr);
> > reasoner.addRules(rules);
> > inf.prepare();
> >
> > The rule added is never triggered.  Is there any way we could add/remove
> > rules programmatically without cause to rebuild InfModal and recheck all
> > the existing rules?
> >
> >
> > Dave Reynolds <dave.e.reynolds@gmail.com
> > <ma...@gmail.com>> 于2023年3月30日周四 14:14写道:
> >
> >     There's no support for dynamically adding forward rules to a
> "running"
> >     reasoner that I recall.
> >
> >     You can dynamically add backward rules to a hybrid reasoner, but
> that's
> >     not relevant here.
> >
> >     Your code example suggests you are calling addRule on a
> >     BasicForwardRuleInfGraph but, unless I'm missing something, that
> method
> >     is not in the jena source. Is it something you have added?
> >
> >     You can add rules to a reasoner but then would essentially have to
> >     build
> >     a new InfGraph or reset the engine state of the current infgraph.
> >     Either
> >     way that's why you are seeing the original deduction retrigger.
> >
> >     If you need to fire some action when adding triples to a model, and
> >     dynamically change the processing, I'd suggest using the listener
> >     machinery rather than the rule system.
> >
> >     Dave
> >
> >     On 28/03/2023 18:43, L B wrote:
> >      > I am currently still trying to figure out the solution.
> >      >
> >      > 1. If I do not call coreInfModel.prepare(), nothing happens
> >     during the
> >      > triple update which is WAD, I believe.
> >      > 2. When one triple is updated, all the rules which have true
> >     condition will
> >      > be triggered, rather than the rules related. I am expecting the
> >     other rules
> >      > will not be triggered unless its related triplets are updated. Is
> it
> >      > possible?
> >      >
> >      >
> >      > L B <daydaydev@gmail.com <ma...@gmail.com>> 于2023年3
> >     月24日周五 16:10写道:
> >      >
> >      >>
> >      >> 1) Init code:
> >      >>
> >      >> GenericRuleReasoner reasoner = new GenericRuleReasoner(new
> >      >> ArrayList<Rule>());
> >      >>              reasoner.setMode(GenericRuleReasoner.FORWARD);
> >      >>              mCoreInfModel =
> ModelFactory.createInfModel(reasoner,
> >      >> mCoreModel);
> >      >>
> >      >> 2) Statement exits
> >      >>   (:A :B :C)
> >      >>
> >      >> 3) Rule exists and triggered before
> >      >>
> >      >>              [ alwaysTrue:
> >      >>                  (:A :B :C)
> >      >>                  -> triggerAction
> >      >>              ]
> >      >>
> >      >> 4) Now, I am trying to add rules at runtime.
> >      >>
> >      >>               [ rule2:
> >      >>                  (:X :Y :Z)
> >      >>                  -> triggerAction2
> >      >>              ]
> >      >>
> >      >>          InfModel infModel = getCoreInfModel();
> >      >>          BasicForwardRuleInfGraph infGraph =
> >     (BasicForwardRuleInfGraph)
> >      >> infModel.getGraph();          infGraph.addRule(rule)
> >      >>          coreInfModel.prepare()
> >      >>
> >      >> 5) add triple (:X :Y :Z)
> >      >>
> >      >>          val infGraph = infModel.graph as
> BasicForwardRuleInfGraph
> >      >>         infGraph.add(triple)
> >      >>
> >      >> *The problem comes, both "alwaysTrue" and "rule2" triggered.*
> >      >>
> >      >> *I am expecting only "rule2" is triggered. *
> >      >>
> >      >> Is there anyway that only "rule2" can be triggered since the
> updated
> >      >> triple is only X, Y Z?
> >      >>
> >      >> Many thanks
> >      >>
> >      >>
> >      >>
> >      >
> >
>

Re: Add rule at runtime, without trigger not-related rule?

Posted by Dave Reynolds <da...@gmail.com>.
Hi,

Very mysterious. If I check the github repository as tagged for 4.2.0 I 
can't see an addRule method in BasicForwardRuleInfGraph. Clearly I'm 
missing something.

You can add rules to a reasoner but you would then have to rebuild the 
InfGraph/InfModel.

Like I say, you can add backward rules to a running hybrid engine, if 
that's any use but I don't believe there's any support for dynamically 
adding forward rules.

Dave

On 03/04/2023 19:52, L B wrote:
> Hi Dave,
> 
> Thanks for the update.
> 
> The Jena version I am using is 4.2.0.
> image.png
> 
> Checked Jena version 4.5.0. This API seems gone.
> 
> The system we are developing may have thousands of rules. To make the 
> engine more efficient,  we want some rules to be loaded/unloaded 
> dynamically.  For example, the driving related rules are loaded when the 
> user is driving. When the user gets home, those driving rules will be 
> unloaded.
> 
> In Jena 4.5 API, the reasoner has addRules API, but I do not know how to 
> make it work.
> 
> //inf is the InfModel created already.
> GenericRuleReasoner reasoner = (GenericRuleReasoner) inf.getReasoner();
> reasoner.setMode(GenericRuleReasoner.HYBRID);
> reasoner.setDerivationLogging(true);
> reasoner.setTraceOn(true);
> 
> List<Rule> rules = Rule.parseRules(ruleStr);
> reasoner.addRules(rules);
> inf.prepare();
> 
> The rule added is never triggered.  Is there any way we could add/remove 
> rules programmatically without cause to rebuild InfModal and recheck all 
> the existing rules?
> 
> 
> Dave Reynolds <dave.e.reynolds@gmail.com 
> <ma...@gmail.com>> 于2023年3月30日周四 14:14写道:
> 
>     There's no support for dynamically adding forward rules to a "running"
>     reasoner that I recall.
> 
>     You can dynamically add backward rules to a hybrid reasoner, but that's
>     not relevant here.
> 
>     Your code example suggests you are calling addRule on a
>     BasicForwardRuleInfGraph but, unless I'm missing something, that method
>     is not in the jena source. Is it something you have added?
> 
>     You can add rules to a reasoner but then would essentially have to
>     build
>     a new InfGraph or reset the engine state of the current infgraph.
>     Either
>     way that's why you are seeing the original deduction retrigger.
> 
>     If you need to fire some action when adding triples to a model, and
>     dynamically change the processing, I'd suggest using the listener
>     machinery rather than the rule system.
> 
>     Dave
> 
>     On 28/03/2023 18:43, L B wrote:
>      > I am currently still trying to figure out the solution.
>      >
>      > 1. If I do not call coreInfModel.prepare(), nothing happens
>     during the
>      > triple update which is WAD, I believe.
>      > 2. When one triple is updated, all the rules which have true
>     condition will
>      > be triggered, rather than the rules related. I am expecting the
>     other rules
>      > will not be triggered unless its related triplets are updated. Is it
>      > possible?
>      >
>      >
>      > L B <daydaydev@gmail.com <ma...@gmail.com>> 于2023年3
>     月24日周五 16:10写道:
>      >
>      >>
>      >> 1) Init code:
>      >>
>      >> GenericRuleReasoner reasoner = new GenericRuleReasoner(new
>      >> ArrayList<Rule>());
>      >>              reasoner.setMode(GenericRuleReasoner.FORWARD);
>      >>              mCoreInfModel = ModelFactory.createInfModel(reasoner,
>      >> mCoreModel);
>      >>
>      >> 2) Statement exits
>      >>   (:A :B :C)
>      >>
>      >> 3) Rule exists and triggered before
>      >>
>      >>              [ alwaysTrue:
>      >>                  (:A :B :C)
>      >>                  -> triggerAction
>      >>              ]
>      >>
>      >> 4) Now, I am trying to add rules at runtime.
>      >>
>      >>               [ rule2:
>      >>                  (:X :Y :Z)
>      >>                  -> triggerAction2
>      >>              ]
>      >>
>      >>          InfModel infModel = getCoreInfModel();
>      >>          BasicForwardRuleInfGraph infGraph =
>     (BasicForwardRuleInfGraph)
>      >> infModel.getGraph();          infGraph.addRule(rule)
>      >>          coreInfModel.prepare()
>      >>
>      >> 5) add triple (:X :Y :Z)
>      >>
>      >>          val infGraph = infModel.graph as BasicForwardRuleInfGraph
>      >>         infGraph.add(triple)
>      >>
>      >> *The problem comes, both "alwaysTrue" and "rule2" triggered.*
>      >>
>      >> *I am expecting only "rule2" is triggered. *
>      >>
>      >> Is there anyway that only "rule2" can be triggered since the updated
>      >> triple is only X, Y Z?
>      >>
>      >> Many thanks
>      >>
>      >>
>      >>
>      >
>